import sys
import os
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'))

import logging
from util import MySQLUtil
from mcp.server.fastmcp import FastMCP
from fastmcp.exceptions import ToolError
from fastmcp.tools.tool import ToolAnnotations
from typing import Annotated
from pydantic import Field
import json

# Initialize FastMCP server with explicit configuration
# - name: Human-readable server identifier
# - mask_error_details: Security setting to control error message exposure
mcp = FastMCP(
    name="xjcmcpServer",
    mask_error_details=True,
    host="0.0.0.0", port=8000  # Hide internal error details from clients
)

db = MySQLUtil()

logger = logging.getLogger("xjcmcp")
logging.basicConfig(level=logging.INFO)

@mcp.tool(
    name="get_graduate_employment_rate",
    description="毕业去向落实率-总体数据获取，根据项目id、问卷id集合、产品ID等参数，获取毕业去向落实率-总体数据。",
    annotations=ToolAnnotations(
        title="毕业去向落实率-总体数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_graduate_employment_rate(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取毕业去向落实率-总体数据。
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]

            # 2. 查询问卷信息
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)

            # 3. 查询该指标对应问题的id
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000366'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]

            # 4. 查询答案表，计算前6项占比之和
            answer_table = f"re_dy_paper_answer_{split_tb}"
            sql_ans = f"SELECT (IFNULL(sum(c1),0)+IFNULL(sum(c2),0)+IFNULL(sum(c3),0)+IFNULL(sum(c4),0)+IFNULL(sum(c5),0)+IFNULL(sum(c6),0))/count(*) as rate FROM {answer_table} WHERE cd_template_id=%s and wid=%s and ans_true=1"
            # 这里wid的获取方式为question_id
            ans_info = db.fetchone(sql_ans, (cd_template_id, question_id))
            rate = ans_info["rate"] if ans_info and ans_info["rate"] is not None else 0

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{rate:.4f}"}
                                ]
                            }
                        ]
                    }
                ]
            }
        
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            # 这里假设客户编码为CL+数字，去除前缀后转为数字字符串
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询落实率：毕业去向合并字段不为'待就业'和'暂不就业'为落实人数，总人数为毕业生人数
            sql = f"""
                SELECT
                  ROUND(
                    (SUM(CASE WHEN grad_dest_merge NOT IN ('待就业', '暂不就业') THEN 1 ELSE 0 END) / COUNT(*)),
                    6
                  ) AS ratio_percent
                FROM {student_table}
                WHERE shard_tb_key = %s
            """
            ans_info = db.fetchone(sql, (shard_tb_key,))
            ratio = ans_info["ratio_percent"] if ans_info and ans_info["ratio_percent"] is not None else 0

            # 5. 返回数据JSON，结构与其他产品一致
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{ratio:.6f}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"毕业去向落实率数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("毕业去向落实率数据获取失败")

@mcp.tool(
    name="get_college_graduate_employment_distribution",
    description="各院系毕业去向落实率分布数据获取，根据项目id、问卷id集合、产品id等参数，获取数据",
    annotations=ToolAnnotations(
        title="各院系毕业去向落实率分布数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_college_graduate_employment_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    各院系毕业去向落实率分布数据获取。
    """
    try:
        # 产品ID分发逻辑
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3.1 确定该问题对应问卷id,根据问题所属调研对象（固定为GRADUATE_SHORT）
            dy_target = "GRADUATE_SHORT"
            sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target=%s"
            target_wt = db.fetchone(sql_target, (dy_target,))
            if not target_wt or not target_wt.get("id"):
                raise ToolError("未找到指定调研对象的问卷id")
            target_wt_id = target_wt["id"]

            # 3.2 查询问题id（固定问题编码T00000366）
            wt_code = 'T00000366'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]

            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

            # 5. 查询答案表，关联学生表，按院系分组统计前6项占比
            answer_table = f"re_dy_paper_answer_{split_tb}"
            student_table = f"dim_client_target_baseinfo_student_{item_year}"
            sql = f"""
                SELECT
                  s.college AS gwd1,
                  (IFNULL(SUM(t1.c1),0)+IFNULL(SUM(t1.c2),0)+IFNULL(SUM(t1.c3),0)+IFNULL(SUM(t1.c4),0)+IFNULL(SUM(t1.c5),0)+IFNULL(SUM(t1.c6),0))/COUNT(*) as ratio
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.college != ''
                GROUP BY
                  s.college
                order by
                  ratio desc
            """
            ans_list = db.fetchall(sql, (target_wt_id, question_id, shard_tb_key, item_year))

            # 6. 组装返回数据
            calcVals = []
            for row in ans_list:
                college = row.get("gwd1")
                ratio = row.get("ratio", 0)
                calcVals.append({
                    "wdValNameList": [college],
                    "val": [
                        {"key": "default", "val": f"{ratio:.6f}"}
                    ]
                })
            result = [{
                "wdKeyNameList": [
                    {"wdKeyCode": "WDK00004", "wdKeyName": "院系"}
                ],
                "calcVals": calcVals
            }]
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": result
            }

        def calc_for_default():
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": ""}
                                ]
                            }
                        ]
                    }
                ]
            }

        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
    except Exception as e:
        logger.error(f"各院系毕业去向落实率分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("各院系毕业去向落实率分布数据获取失败")

@mcp.tool(
    name="get_graduate_employment_option_distribution",
    description="毕业去向分布占比分析数据获取，根据项目id、问卷id集合、产品id，统计各选项占比。",
    annotations=ToolAnnotations(
        title="毕业去向分布占比分析数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_graduate_employment_option_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    毕业去向分布占比分析数据获取
    入参：项目id、问卷id集合、产品id
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询该指标对应问题的id（固定问题编码T00000366）
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000366'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.1 解析wt_obj字段，提取选项描述
            import json
            try:
                wt_obj_json = json.loads(wt_obj)
                item_list = wt_obj_json.get("itemList", [])
                option_map = {item["key"]: item["val"] for item in item_list if "key" in item and "val" in item}
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 查询答案表，计算所有选项的占比
            answer_table = f"re_dy_paper_answer_{split_tb}"
            sql_ans = f"""
                SELECT
                    SUM(c1)/COUNT(*) AS ratio_c1,
                    SUM(c2)/COUNT(*) AS ratio_c2,
                    SUM(c3)/COUNT(*) AS ratio_c3,
                    SUM(c4)/COUNT(*) AS ratio_c4,
                    SUM(c5)/COUNT(*) AS ratio_c5,
                    SUM(c6)/COUNT(*) AS ratio_c6,
                    SUM(c7)/COUNT(*) AS ratio_c7
                FROM {answer_table}
                WHERE cd_template_id=%s AND wid=%s AND ans_true=1
            """
            ans_info = db.fetchone(sql_ans, (cd_template_id, question_id))
            if not ans_info:
                raise ToolError("未找到答案数据")

            # 5. 组装返回数据，key为选项描述，val为占比
            option_keys = ["1", "2", "3", "4", "5", "6", "7"]
            result_val = []
            for idx, key in enumerate(option_keys, 1):
                desc = option_map.get(key, f"选项{key}")
                ratio = ans_info.get(f"ratio_c{idx}", 0)
                result_val.append({"key": desc, "val": f"{ratio:.6f}"})

            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": result_val
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": []
                            }
                        ]
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
    except Exception as e:
        logger.error(f"毕业去向各选项占比分析异常: {str(e)}", exc_info=True)
        raise ToolError("毕业去向各选项占比分析失败")

@mcp.tool(
    name="get_employment_opportunity_sufficiency",
    description="就业机会充分度分析数据获取，根据项目id、问卷id集合，统计各选项占比。",
    annotations=ToolAnnotations(
        title="就业机会充分度分析数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_employment_opportunity_sufficiency(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    就业机会充分度分析数据获取
    入参：项目id、问卷id集合
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id（固定问题编码T00000373）
        cd_template_id = questionnaire_ids[0]
        wt_code = 'T00000373'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.1 解析wt_obj字段，提取选项描述
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            option_map = {item["key"]: item["val"] for item in item_list if "key" in item and "val" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算所有选项的占比（分母为前五项之和）
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql_ans = f"""
            SELECT
                SUM(c1)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c1,
                SUM(c2)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c2,
                SUM(c3)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c3,
                SUM(c4)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c4,
                SUM(c5)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c5
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid=%s AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (cd_template_id, question_id))
        if not ans_info:
            raise ToolError("未找到答案数据")

        # 5. 组装返回数据，key为选项描述，val为占比
        option_keys = ["1", "2", "3", "4", "5"]
        result_val = []
        for idx, key in enumerate(option_keys, 1):
            desc = option_map.get(key, f"选项{key}")
            ratio = ans_info.get(f"ratio_c{idx}", 0)
            result_val.append({"key": desc, "val": f"{ratio:.6f}"})

        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": result_val
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"就业机会充分度分析异常: {str(e)}", exc_info=True)
        raise ToolError("就业机会充分度分析失败")

@mcp.tool(
    name="get_province_internal_employment_city_distribution",
    description="省内就业城市分布（前十）数据获取，根据项目id、问卷id集合、产品id，统计省内就业学生的城市分布（前十）",
    annotations=ToolAnnotations(
        title="省内就业城市分布（前十）数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_province_internal_employment_city_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    省内就业城市分布（前十）数据获取
    入参：项目id、问卷id集合、产品id
    """
    try:
        def calc_for_pd0038():
            # 原有全部逻辑
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询该指标对应问题的id（固定问题编码T00000370）
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000370'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.1 解析wt_obj字段，提取relationData，确认第二级为市
            import json
            try:
                wt_obj_json = json.loads(wt_obj)
                relation_data = wt_obj_json.get("relationData", [])
                # 默认第二级为市
                if len(relation_data) < 2:
                    raise ToolError("relationData结构异常，未找到市级")
                city_key = relation_data[1]["key"]
                city_val = relation_data[1]["val"]
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

            # 5. 查询答案表，获取省内就业学生的answer_obj，关联学生表
            answer_table = f"re_dy_paper_answer_{split_tb}"
            student_table = f"dim_client_target_baseinfo_student_{item_year}"
            sql = f"""
                SELECT
                  t1.answer_obj AS obj_wt
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.job_around = '省内就业'
            """
            ans_list = db.fetchall(sql, (cd_template_id, question_id, shard_tb_key, item_year))
            if not ans_list:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "calcVals": [
                                {
                                    "wdValNameList": [],
                                    "val": []
                                }
                            ]
                        }
                    ]
                }

            # 6. 统计第二级（市）分布
            from collections import Counter
            city_counter = Counter()
            total = 0
            for row in ans_list:
                obj_str = row.get("obj_wt")
                try:
                    obj = json.loads(obj_str)
                    if isinstance(obj, list) and len(obj) > 1:
                        city = obj[1].get("wv")
                        if city:
                            city_counter[city] += 1
                            total += 1
                except Exception as e:
                    logger.warning(f"answer_obj解析失败: {obj_str}, 错误: {str(e)}")
                    continue
            if total == 0:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "calcVals": [
                                {
                                    "wdValNameList": [],
                                    "val": []
                                }
                            ]
                        }
                    ]
                }
            # 7. 取前十城市，计算占比
            top_cities = city_counter.most_common(10)
            result_val = []
            for city, count in top_cities:
                ratio = count / total if total else 0
                result_val.append({"key": city, "val": f"{ratio:.4f}"})

            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "wdValNameList": [],
                                "val": result_val
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            # 默认返回空结构
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "wdValNameList": [],
                                "val": []
                            }
                        ]
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"省内就业城市分布（前十）数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("省内就业城市分布（前十）数据获取失败")

@mcp.tool(
    name="get_college_employment_area_distribution",
    description="各院系就业地区分布（前五，就业省拼接就业市）数据获取，根据项目id、问卷id集合、产品id，统计各院系就业地区分布（前五，就业省拼接就业市）",
    annotations=ToolAnnotations(
        title="各院系就业地区分布（前五，就业省拼接就业市）数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_college_employment_area_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    各院系就业地区分布（前五，就业省拼接就业市）数据获取。
    入参：项目id、问卷id集合、产品id
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询该指标对应问题的id（固定问题编码T00000370）
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000370'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.1 解析wt_obj字段，确认relationData结构
            import json
            try:
                wt_obj_json = json.loads(wt_obj)
                relation_data = wt_obj_json.get("relationData", [])
                if len(relation_data) < 2:
                    raise ToolError("relationData结构异常，未找到省市级")
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

            # 5. 查询答案表，获取省分布（第一级），按院系分组
            answer_table = f"re_dy_paper_answer_{split_tb}"
            student_table = f"dim_client_target_baseinfo_student_{item_year}"
            sql = f"""
                SELECT
                  s.college AS gwd1,
                  t1.answer_obj AS obj_wt
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.college != ''
            """
            ans_list = db.fetchall(sql, (cd_template_id, question_id, shard_tb_key, item_year))
            # 5.1 统计各院系下省分布
            from collections import defaultdict, Counter
            college_province_counter = defaultdict(Counter)
            college_total = defaultdict(int)
            for row in ans_list:
                college = row.get("gwd1")
                obj_str = row.get("obj_wt")
                try:
                    obj = json.loads(obj_str)
                    if isinstance(obj, list) and len(obj) > 0:
                        province = obj[0].get("wv")
                        if province:
                            college_province_counter[college][province] += 1
                            college_total[college] += 1
                except Exception as e:
                    logger.warning(f"answer_obj解析失败: {obj_str}, 错误: {str(e)}")
                    continue
            # 5.2 组装省分布结果，取前五
            province_result = []
            for college, prov_counter in college_province_counter.items():
                total = college_total[college]
                top_provs = prov_counter.most_common(5)
                val = []
                for prov, count in top_provs:
                    ratio = count / total if total else 0
                    val.append({"key": prov, "val": f"{ratio:.4f}"})
                province_result.append({
                    "wdValNameList": [college],
                    "val": val
                })
            # 6. 查询答案表，获取省内就业城市分布（第二级），按院系分组
            sql_city = f"""
                SELECT
                  s.college AS gwd1,
                  t1.answer_obj AS obj_wt
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.college != ''
                  AND s.job_around IN ('省内就业')
            """
            ans_city_list = db.fetchall(sql_city, (cd_template_id, question_id, shard_tb_key, item_year))
            college_city_counter = defaultdict(Counter)
            college_city_total = defaultdict(int)
            for row in ans_city_list:
                college = row.get("gwd1")
                obj_str = row.get("obj_wt")
                try:
                    obj = json.loads(obj_str)
                    if isinstance(obj, list) and len(obj) > 1:
                        city = obj[1].get("wv")
                        if city:
                            college_city_counter[college][city] += 1
                            college_city_total[college] += 1
                except Exception as e:
                    logger.warning(f"answer_obj解析失败: {obj_str}, 错误: {str(e)}")
                    continue
            # 6.1 组装城市分布结果，取前五
            city_result = []
            for college, city_counter in college_city_counter.items():
                total = college_city_total[college]
                top_cities = city_counter.most_common(5)
                val = []
                for city, count in top_cities:
                    ratio = count / total if total else 0
                    val.append({"key": city, "val": f"{ratio:.4f}"})
                city_result.append({
                    "wdValNameList": [college],
                    "val": val
                })
            # 7. 返回数据JSON，分别返回省分布和省内就业城市分布
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "院系"}
                        ],
                        "calcVals": province_result
                    },
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "院系"}
                        ],
                        "calcVals": city_result
                    }
                ]
            }

        def calc_for_default():
            # 默认返回空结构
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "wdValNameList": [],
                                "val": []
                            }
                        ]
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"各院系就业地区分布（前五，就业省拼接就业市）数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("各院系就业地区分布（前五，就业省拼接就业市）数据获取失败")

@mcp.tool(
    name="get_province_internal_student_employment_distribution",
    description="省内生源就业地分布数据获取，根据项目id、问卷id集合、产品id，统计省内生源学生的就业地分布（省内/省外）",
    annotations=ToolAnnotations(
        title="省内生源就业地分布数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_province_internal_student_employment_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    省内生源就业地分布数据获取。
    入参：项目id、问卷id集合、产品id
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询该指标对应问题的id（固定问题编码T00000370）
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000370'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.1 解析wt_obj字段，确认relationData结构，第二级为市
            import json
            try:
                wt_obj_json = json.loads(wt_obj)
                relation_data = wt_obj_json.get("relationData", [])
                if len(relation_data) < 2:
                    raise ToolError("relationData结构异常，未找到省市级")
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

            # 5. 查询答案表，关联学生表，筛选省内生源，统计就业地分布（省内/省外），按job_around分组
            answer_table = f"re_dy_paper_answer_{split_tb}"
            student_table = f"dim_client_target_baseinfo_student_{item_year}"
            sql = f"""
                SELECT
                  s.job_around AS gwd1,
                  COUNT(*) AS count_size
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.job_around != ''
                  AND s.birth_around IN ('省内生源')
                GROUP BY
                  s.job_around
            """
            ans_list = db.fetchall(sql, (cd_template_id, question_id, shard_tb_key, item_year))
            if not ans_list:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "wdKeyNameList": [
                                {"wdKeyName": "就业省内外"}
                            ],
                            "calcVals": []
                        }
                    ]
                }
            # 6. 统计省内/省外就业人数
            total = sum(row.get("count_size", 0) for row in ans_list)
            result_vals = []
            for row in ans_list:
                job_around = row.get("gwd1", "")
                count = row.get("count_size", 0)
                if not job_around:
                    continue
                ratio = count / total if total else 0
                result_vals.append({
                    "wdValNameList": [job_around],
                    "val": [
                        {"key": "default", "val": f"{ratio:.4f}"}
                    ]
                })
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "就业省内外"}
                        ],
                        "calcVals": result_vals
                    }
                ]
            }

        def calc_for_default():
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "就业省内外"}
                        ],
                        "calcVals": []
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
    except Exception as e:
        logger.error(f"省内生源就业地分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("省内生源就业地分布数据获取失败")

@mcp.tool(
    name="get_province_external_student_employment_distribution",
    description="省外生源就业地分布数据获取，根据项目id、问卷id集合、产品id，统计省外生源学生的就业地分布（省内/省外）",
    annotations=ToolAnnotations(
        title="省外生源就业地分布数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_province_external_student_employment_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    省外生源就业地分布数据获取。
    入参：项目id、问卷id集合、产品id
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询该指标对应问题的id（固定问题编码T00000370）
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000370'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.1 解析wt_obj字段，确认relationData结构，第二级为市
            import json
            try:
                wt_obj_json = json.loads(wt_obj)
                relation_data = wt_obj_json.get("relationData", [])
                if len(relation_data) < 2:
                    raise ToolError("relationData结构异常，未找到省市级")
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

            # 5. 查询答案表，关联学生表，筛选省外生源，统计就业地分布（省内/省外），按job_around分组
            answer_table = f"re_dy_paper_answer_{split_tb}"
            student_table = f"dim_client_target_baseinfo_student_{item_year}"
            sql = f"""
                SELECT
                  s.job_around AS gwd1,
                  COUNT(*) AS count_size
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.job_around != ''
                  AND s.birth_around IN ('省外生源')
                GROUP BY
                  s.job_around
            """
            ans_list = db.fetchall(sql, (cd_template_id, question_id, shard_tb_key, item_year))
            if not ans_list:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "wdKeyNameList": [
                                {"wdKeyName": "就业省内外"}
                            ],
                            "calcVals": []
                        }
                    ]
                }
            # 6. 统计省内/省外就业人数
            total = sum(row.get("count_size", 0) for row in ans_list)
            result_vals = []
            for row in ans_list:
                job_around = row.get("gwd1", "")
                count = row.get("count_size", 0)
                if not job_around:
                    continue
                ratio = count / total if total else 0
                result_vals.append({
                    "wdValNameList": [job_around],
                    "val": [
                        {"key": "default", "val": f"{ratio:.4f}"}
                    ]
                })
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "就业省内外"}
                        ],
                        "calcVals": result_vals
                    }
                ]
            }

        def calc_for_default():
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "就业省内外"}
                        ],
                        "calcVals": []
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"省外生源就业地分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("省外生源就业地分布数据获取失败")

@mcp.tool(
    name="get_main_employment_area_avg_income",
    description="主要就业地区月均收入数据获取，根据项目id、问卷id集合、产品ID等参数，统计各就业省份的平均月收入。",
    annotations=ToolAnnotations(
        title="主要就业地区月均收入数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_main_employment_area_avg_income(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    主要就业地区月均收入数据获取。
    入参：项目id、问卷id集合、产品id
    结合T00000374题的平均薪酬，和答了T00000370这道题的人的就业省，最后按就业省分布，取平均薪酬，来反应各就业省平均薪酬分布。
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询T00000370（就业地）和T00000374（平均月收入）问题id和结构
            cd_template_id = questionnaire_ids[0]
            # 就业地
            sql_q_area = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_area_info = db.fetchone(sql_q_area, (cd_template_id, 'T00000370'))
            if not q_area_info or not q_area_info.get("id"):
                raise ToolError("未找到就业地问题id")
            area_question_id = q_area_info["id"]
            area_wt_obj = q_area_info.get("wt_obj")
            if not area_wt_obj:
                raise ToolError("未找到就业地问题选项描述")
            # 平均月收入
            sql_q_income = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_income_info = db.fetchone(sql_q_income, (cd_template_id, 'T00000374'))
            if not q_income_info or not q_income_info.get("id"):
                raise ToolError("未找到平均月收入问题id")
            income_question_id = q_income_info["id"]
            income_wt_obj = q_income_info.get("wt_obj")
            if not income_wt_obj:
                raise ToolError("未找到平均月收入问题选项描述")
            # 3.1 解析就业地wt_obj，确认relationData结构，第二级为市，第一级为省
            import json
            try:
                area_wt_obj_json = json.loads(area_wt_obj)
                relation_data = area_wt_obj_json.get("relationData", [])
                if len(relation_data) < 1:
                    raise ToolError("relationData结构异常，未找到省级")
            except Exception as e:
                logger.error(f"就业地wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("就业地问题选项描述解析失败")
            # 4. 解析shard_tb_key（client_code如CL0002→2），拼接item_year
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"
            # 5. 查询答案表，关联两道题的答案，按就业省分组，统计平均薪酬
            answer_table = f"re_dy_paper_answer_{split_tb}"
            # t1: 平均月收入，t2: 就业地
            sql = f"""
                SELECT
                    t2.answer_obj AS area_obj,
                    t1.n1 AS income
                FROM {answer_table} t1
                    JOIN {answer_table} t2 ON t1.target_no = t2.target_no
                WHERE
                    t1.shard_tb_key = %s
                    AND t1.cd_template_id = %s
                    AND t1.wid = %s
                    AND t1.ans_true = 1
                    AND t2.shard_tb_key = %s
                    AND t2.cd_template_id = %s
                    AND t2.wid = %s
                    AND t2.ans_true = 1
            """
            # income_question_id为T00000374的wid，area_question_id为T00000370的wid
            ans_list = db.fetchall(sql, (
                shard_tb_key, cd_template_id, income_question_id,
                shard_tb_key, cd_template_id, area_question_id
            ))
            if not ans_list:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "wdKeyNameList": [],
                            "calcVals": []
                        }
                    ]
                }
            # 6. 统计各就业省的平均薪酬
            from collections import defaultdict
            province_income = defaultdict(list)
            for row in ans_list:
                area_obj_str = row.get("area_obj")
                income = row.get("income")
                try:
                    area_obj = json.loads(area_obj_str)
                    if isinstance(area_obj, list) and len(area_obj) > 0:
                        province = area_obj[0].get("wv")
                        if province and income is not None and str(income).strip() != '':
                            try:
                                income_val = float(income)
                                province_income[province].append(income_val)
                            except Exception:
                                continue
                except Exception as e:
                    logger.warning(f"answer_obj解析失败: {area_obj_str}, 错误: {str(e)}")
                    continue
            # 7. 组装返回数据
            calcVals = []
            for province, incomes in province_income.items():
                if not incomes:
                    continue
                avg_income = sum(incomes) / len(incomes)
                calcVals.append({
                    "wdValNameList": [province],
                    "val": [
                        {"key": "default", "val": f"{avg_income:.2f}"}
                    ]
                })
            # 按平均薪酬降序排列
            calcVals.sort(key=lambda x: float(x["val"][0]["val"]), reverse=True)
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [],
                        "calcVals": calcVals
                    }
                ]
            }

        def calc_for_default():
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [],
                        "calcVals": []
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"主要就业地区月均收入数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("主要就业地区月均收入数据获取失败")

@mcp.tool(
    name="get_college_main_employment_industry_distribution",
    description="各院系主要就业行业分布数据获取，根据项目id、问卷id集合、产品id，统计各院系主要就业行业分布（前五，行业门类）",
    annotations=ToolAnnotations(
        title="各院系主要就业行业分布数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_college_main_employment_industry_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    各院系主要就业行业分布数据获取。
    入参：项目id、问卷id集合、产品id
    统计T00000371（就业行业）题的第一级（行业门类）分布，按院系分组，返回前五。
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询该指标对应问题的id（固定问题编码T00000371）
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000371'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.1 解析wt_obj字段，确认relationData结构，第一级为行业门类
            import json
            try:
                wt_obj_json = json.loads(wt_obj)
                relation_data = wt_obj_json.get("relationData", [])
                if len(relation_data) < 1:
                    raise ToolError("relationData结构异常，未找到行业门类")
                industry_key = relation_data[0]["key"]
                industry_val = relation_data[0]["val"]
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

            # 5. 查询答案表，获取就业行业分布（第一级），按院系分组
            answer_table = f"re_dy_paper_answer_{split_tb}"
            student_table = f"dim_client_target_baseinfo_student_{item_year}"
            sql = f"""
                SELECT
                  s.college AS gwd1,
                  t1.answer_obj AS obj_wt
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.college != ''
            """
            ans_list = db.fetchall(sql, (cd_template_id, question_id, shard_tb_key, item_year))
            # 5.1 统计各院系下行业门类分布
            from collections import defaultdict, Counter
            college_industry_counter = defaultdict(Counter)
            college_total = defaultdict(int)
            for row in ans_list:
                college = row.get("gwd1")
                obj_str = row.get("obj_wt")
                try:
                    obj = json.loads(obj_str)
                    if isinstance(obj, list) and len(obj) > 0:
                        industry = obj[0].get("wv")
                        if industry:
                            college_industry_counter[college][industry] += 1
                            college_total[college] += 1
                except Exception as e:
                    logger.warning(f"answer_obj解析失败: {obj_str}, 错误: {str(e)}")
                    continue
            # 5.2 组装行业分布结果，取前五
            industry_result = []
            for college, ind_counter in college_industry_counter.items():
                total = college_total[college]
                top_inds = ind_counter.most_common(5)
                val = []
                for ind, count in top_inds:
                    ratio = count / total if total else 0
                    val.append({"key": ind, "val": f"{ratio:.4f}"})
                industry_result.append({
                    "wdValNameList": [college],
                    "val": val
                })
            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "院系"}
                        ],
                        "calcVals": industry_result
                    }
                ]
            }

        def calc_for_default():
            # 查询院系列表（可选，或直接返回空结构）
            # 这里返回结构与calc_for_pd0038一致，但val为空
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "院系"}
                        ],
                        "calcVals": []
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"各院系主要就业行业分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("各院系主要就业行业分布数据获取失败")

@mcp.tool(
    name="get_employment_quality_by_industry",
    description="不同就业行业就业质量差异分析数据获取，根据项目id、问卷id集合、产品id，统计各行业的平均薪酬、工作满意度、离职率。",
    annotations=ToolAnnotations(
        title="不同就业行业就业质量差异分析数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_employment_quality_by_industry(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    不同就业行业就业质量差异分析数据获取。
    入参：项目id、问卷id集合、产品id
    统计各行业的平均薪酬、工作满意度、离职率。
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            item_year = proj_info["item_year"]
            client_code = proj_info["client_code"]

            # 2. 查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                raise ToolError("未找到问卷信息")

            # 3. 查询相关题目的id和结构
            cd_template_id = questionnaire_ids[0]
            # 行业题 T00000371
            sql_q_industry = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_industry_info = db.fetchone(sql_q_industry, (cd_template_id, 'T00000371'))
            if not q_industry_info or not q_industry_info.get("id"):
                raise ToolError("未找到行业题id")
            industry_question_id = q_industry_info["id"]
            industry_wt_obj = q_industry_info.get("wt_obj")
            # 薪酬题 T00000374
            q_income_info = db.fetchone(sql_q_industry, (cd_template_id, 'T00000374'))
            if not q_income_info or not q_income_info.get("id"):
                raise ToolError("未找到薪酬题id")
            income_question_id = q_income_info["id"]
            # 满意度题 T00000376
            q_satisfaction_info = db.fetchone(sql_q_industry, (cd_template_id, 'T00000376'))
            if not q_satisfaction_info or not q_satisfaction_info.get("id"):
                raise ToolError("未找到满意度题id")
            satisfaction_question_id = q_satisfaction_info["id"]
            # 离职次数题 T00000385
            q_leave_info = db.fetchone(sql_q_industry, (cd_template_id, 'T00000385'))
            if not q_leave_info or not q_leave_info.get("id"):
                raise ToolError("未找到离职次数题id")
            leave_question_id = q_leave_info["id"]

            # 3.1 解析行业题wt_obj，获取relationData第一级（行业门类）
            import json
            try:
                industry_wt_obj_json = json.loads(industry_wt_obj)
                relation_data = industry_wt_obj_json.get("relationData", [])
                if len(relation_data) < 1:
                    raise ToolError("relationData结构异常，未找到行业门类")
            except Exception as e:
                logger.error(f"行业题wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("行业题选项描述解析失败")

            # 4. 解析shard_tb_key（client_code如CL0002→2），拼接item_year
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"
            answer_table = f"re_dy_paper_answer_{split_tb}"

            # 5. 计算各行业平均薪酬
            sql_income = f"""
                SELECT
                    t2.answer_obj AS industry_obj,
                    t1.n1 AS income
                FROM {answer_table} t1
                    JOIN {answer_table} t2 ON t1.target_no = t2.target_no
                WHERE
                    t1.shard_tb_key = %s
                    AND t1.cd_template_id = %s
                    AND t1.wid = %s
                    AND t1.ans_true = 1
                    AND t2.shard_tb_key = %s
                    AND t2.cd_template_id = %s
                    AND t2.wid = %s
                    AND t2.ans_true = 1
            """
            income_list = db.fetchall(sql_income, (
                shard_tb_key, cd_template_id, income_question_id,
                shard_tb_key, cd_template_id, industry_question_id
            ))
            from collections import defaultdict
            industry_income = defaultdict(list)
            for row in income_list:
                industry_obj_str = row.get("industry_obj")
                income = row.get("income")
                try:
                    industry_obj = json.loads(industry_obj_str)
                    if isinstance(industry_obj, list) and len(industry_obj) > 0:
                        industry = industry_obj[0].get("wv")
                        if industry and income is not None and str(income).strip() != '':
                            try:
                                income_val = float(income)
                                industry_income[industry].append(income_val)
                            except Exception:
                                continue
                except Exception as e:
                    logger.warning(f"industry_obj解析失败: {industry_obj_str}, 错误: {str(e)}")
                    continue
            industry_avg_income = {k: (sum(v)/len(v) if v else 0) for k, v in industry_income.items()}

            # 6. 计算各行业满意度（前三项占比之和/前五项总和，去除第6项）
            sql_satisfaction = f"""
                SELECT
                    t2.answer_obj AS industry_obj,
                    t1.c1, t1.c2, t1.c3, t1.c4, t1.c5
                FROM {answer_table} t1
                    JOIN {answer_table} t2 ON t1.target_no = t2.target_no
                WHERE
                    t1.shard_tb_key = %s
                    AND t1.cd_template_id = %s
                    AND t1.wid = %s
                    AND t1.ans_true = 1
                    AND t2.shard_tb_key = %s
                    AND t2.cd_template_id = %s
                    AND t2.wid = %s
                    AND t2.ans_true = 1
            """
            satisfaction_list = db.fetchall(sql_satisfaction, (
                shard_tb_key, cd_template_id, satisfaction_question_id,
                shard_tb_key, cd_template_id, industry_question_id
            ))
            industry_satisfaction = defaultdict(lambda: [0, 0, 0, 0, 0])
            for row in satisfaction_list:
                industry_obj_str = row.get("industry_obj")
                try:
                    industry_obj = json.loads(industry_obj_str)
                    if isinstance(industry_obj, list) and len(industry_obj) > 0:
                        industry = industry_obj[0].get("wv")
                        if industry:
                            c1 = row.get("c1", 0) or 0
                            c2 = row.get("c2", 0) or 0
                            c3 = row.get("c3", 0) or 0
                            c4 = row.get("c4", 0) or 0
                            c5 = row.get("c5", 0) or 0
                            industry_satisfaction[industry][0] += c1
                            industry_satisfaction[industry][1] += c2
                            industry_satisfaction[industry][2] += c3
                            industry_satisfaction[industry][3] += c4
                            industry_satisfaction[industry][4] += c5
                except Exception as e:
                    logger.warning(f"industry_obj解析失败: {industry_obj_str}, 错误: {str(e)}")
                    continue
            industry_satisfaction_ratio = {}
            for k, v in industry_satisfaction.items():
                total = sum(v)
                top3 = v[0] + v[1] + v[2]
                ratio = top3 / total if total else 0
                industry_satisfaction_ratio[k] = ratio

            # 7. 计算各行业离职率（选项2,3,4的占比之和/前四项总和）
            sql_leave = f"""
                SELECT
                    t2.answer_obj AS industry_obj,
                    t1.c1, t1.c2, t1.c3, t1.c4
                FROM {answer_table} t1
                    JOIN {answer_table} t2 ON t1.target_no = t2.target_no
                WHERE
                    t1.shard_tb_key = %s
                    AND t1.cd_template_id = %s
                    AND t1.wid = %s
                    AND t1.ans_true = 1
                    AND t2.shard_tb_key = %s
                    AND t2.cd_template_id = %s
                    AND t2.wid = %s
                    AND t2.ans_true = 1
            """
            leave_list = db.fetchall(sql_leave, (
                shard_tb_key, cd_template_id, leave_question_id,
                shard_tb_key, cd_template_id, industry_question_id
            ))
            industry_leave = defaultdict(lambda: [0, 0, 0, 0])
            for row in leave_list:
                industry_obj_str = row.get("industry_obj")
                try:
                    industry_obj = json.loads(industry_obj_str)
                    if isinstance(industry_obj, list) and len(industry_obj) > 0:
                        industry = industry_obj[0].get("wv")
                        if industry:
                            c1 = row.get("c1", 0) or 0
                            c2 = row.get("c2", 0) or 0
                            c3 = row.get("c3", 0) or 0
                            c4 = row.get("c4", 0) or 0
                            industry_leave[industry][0] += c1
                            industry_leave[industry][1] += c2
                            industry_leave[industry][2] += c3
                            industry_leave[industry][3] += c4
                except Exception as e:
                    logger.warning(f"industry_obj解析失败: {industry_obj_str}, 错误: {str(e)}")
                    continue
            industry_leave_ratio = {}
            for k, v in industry_leave.items():
                total = sum(v)
                leave = v[1] + v[2] + v[3]
                ratio = leave / total if total else 0
                industry_leave_ratio[k] = ratio

            # 8. 组装返回数据
            result = []
            for industry in set(list(industry_avg_income.keys()) + list(industry_satisfaction_ratio.keys()) + list(industry_leave_ratio.keys())):
                result.append({
                    "wdValNameList": [industry],
                    "val": [
                        {"key": "平均薪酬", "val": f"{industry_avg_income.get(industry, 0):.2f}"},
                        {"key": "满意度", "val": f"{industry_satisfaction_ratio.get(industry, 0):.4f}"},
                        {"key": "离职率", "val": f"{industry_leave_ratio.get(industry, 0):.4f}"}
                    ]
                })
            # 按平均薪酬降序排列
            result.sort(key=lambda x: float(x["val"][0]["val"]), reverse=True)
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "行业门类"}
                        ],
                        "calcVals": result
                    }
                ]
            }

        def calc_for_default():
            
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyName": "行业门类"}
                        ],
                        "calcVals": []
                    }
                ]
            }

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"不同就业行业就业质量差异分析异常: {str(e)}", exc_info=True)
        raise ToolError("不同就业行业就业质量差异分析失败")

@mcp.tool(
    name="get_domestic_further_study_comparison",
    description="境内升学率对比分析（和全国对比，定性分析）数据获取，根据今年、上年、前年项目id及问卷id集合，返回各届次及全国的境内升学率占比。",
    annotations=ToolAnnotations(
        title="境内升学率对比分析（和全国对比，定性分析）数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_domestic_further_study_comparison(
    current_project_id: Annotated[int, Field(description="当前年份项目id")],
    current_questionnaire_ids: Annotated[list[int], Field(description="当前年份本项目对应问卷id集合")],
    last_project_id: Annotated[int, Field(description="上年项目id")],
    last_questionnaire_ids: Annotated[list[int], Field(description="上年项目对应问卷id集合")],
    prev_project_id: Annotated[int, Field(description="前年项目id")],
    prev_questionnaire_ids: Annotated[list[int], Field(description="前年项目对应问卷id集合")],
) -> dict:
    """
    境内升学率对比分析（和全国对比，定性分析）数据获取。
    入参：今年、上年、前年项目id及问卷id集合。
    返回：各届次及全国的境内升学率占比。
    """
    import json
    try:
        # 定义一个内部函数用于获取某一项目的境内升学率
        def get_rate(project_id, questionnaire_ids):
            if not project_id or not questionnaire_ids or questionnaire_ids[0] == 0:
                return None
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                return None
            split_tb = proj_info["split_tb_paper"]
            # 2. 查询问卷信息
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            if not wt_info:
                return None
            # 3. 查询该指标对应问题的id（固定问题编码T00000366）
            cd_template_id = questionnaire_ids[0]
            wt_code = 'T00000366'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                return None
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            # 3.1 解析wt_obj字段，提取选项描述
            option_map = {}
            if wt_obj:
                try:
                    wt_obj_json = json.loads(wt_obj)
                    item_list = wt_obj_json.get("itemList", [])
                    option_map = {item["key"]: item["val"] for item in item_list if "key" in item and "val" in item}
                except Exception:
                    pass
            # 4. 查询答案表，计算境内升学（c2）占比
            answer_table = f"re_dy_paper_answer_{split_tb}" if split_tb != '0' else "re_dy_paper_answer"
            sql_ans = f"SELECT SUM(c2) as c2, SUM(c1) as c1, SUM(c3) as c3, SUM(c4) as c4, SUM(c5) as c5, SUM(c6) as c6, SUM(c7) as c7 FROM {answer_table} WHERE cd_template_id=%s and wid=%s and ans_true=1"
            ans_info = db.fetchone(sql_ans, (cd_template_id, question_id))
            if not ans_info:
                return None
            total = sum(ans_info.get(f"c{i}", 0) or 0 for i in range(1, 8))
            c2 = ans_info.get("c2", 0) or 0
            rate = c2 / total if total else 0
            return rate
        # 获取当前、上年、前年升学率
        current_rate = get_rate(current_project_id, current_questionnaire_ids)
        last_rate = get_rate(last_project_id, last_questionnaire_ids)
        prev_rate = get_rate(prev_project_id, prev_questionnaire_ids)
        # 获取当前年份
        sql_proj = "SELECT item_year FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (current_project_id,))
        item_year = proj_info["item_year"] if proj_info and proj_info.get("item_year") else None
        # 获取全国数据
        national_rate = None
        if item_year:
            sql_national = f"SELECT SUM(c2) as c2, SUM(c1) as c1, SUM(c3) as c3, SUM(c4) as c4, SUM(c5) as c5, SUM(c6) as c6, SUM(c7) as c7 FROM dp_bi.dy_all_paper_answer_T00000366 WHERE wt_code='T00000366' AND item_year=%s"
            ans_info = db.fetchone(sql_national, (str(item_year),))
            if ans_info:
                total = sum(ans_info.get(f"c{i}", 0) or 0 for i in range(1, 8))
                c2 = ans_info.get("c2", 0) or 0
                national_rate = c2 / total if total else 0
        # 5. 组装返回数据
        result = {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "year": str(int(item_year)-2) if item_year else "前年",
                    "rate": f"{(prev_rate*100):.2f}%" if prev_rate is not None else None
                },
                {
                    "year": str(int(item_year)-1) if item_year else "上年",
                    "rate": f"{(last_rate*100):.2f}%" if last_rate is not None else None
                },
                {
                    "year": str(item_year) if item_year else "今年",
                    "rate": f"{(current_rate*100):.2f}%" if current_rate is not None else None
                },
                {
                    "year": f"{item_year}全国" if item_year else "全国",
                    "rate": f"{(national_rate*100):.2f}%" if national_rate is not None else None
                }
            ]
        }
        return result
    except Exception as e:
        logger.error(f"境内升学率对比分析数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("境内升学率对比分析数据获取失败")

@mcp.tool(
    name="get_study_abroad_admission_satisfaction",
    description="留学录取结果满意度数据获取，根据项目id、问卷id集合，统计各满意度选项占比",
    annotations=ToolAnnotations(
        title="留学录取结果满意度数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_study_abroad_admission_satisfaction(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    留学录取结果满意度数据获取。
    入参：项目id、问卷id集合
    统计满意度题各选项占比（不含"无法评价"）。
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id（固定问题编码T00000661）
        cd_template_id = questionnaire_ids[0]
        wt_code = 'T00000661'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.1 解析wt_obj字段，提取选项描述（排除key为6的"无法评价"）
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: item["val"] for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算所有选项的占比（分母为前五项之和）
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql_ans = f"""
            SELECT
                SUM(c1)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c1,
                SUM(c2)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c2,
                SUM(c3)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c3,
                SUM(c4)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c4,
                SUM(c5)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) AS ratio_c5
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid=%s AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (cd_template_id, question_id))
        if not ans_info:
            raise ToolError("未找到答案数据")

        # 5. 组装返回数据，key为选项描述，val为占比
        option_keys = ["1", "2", "3", "4", "5"]
        result_val = []
        for idx, key in enumerate(option_keys, 1):
            desc = option_map.get(key, f"选项{key}")
            ratio = ans_info.get(f"ratio_c{idx}", 0)
            result_val.append({"key": desc, "val": f"{ratio:.6f}"})

        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": result_val
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"留学录取结果满意度数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("留学录取结果满意度数据获取失败")

@mcp.tool(
    name="get_college_course_evaluation",
    description="各院系毕业生对所学课程的评价（多指标分析）数据获取，根据项目id、问卷id集合，统计各院系的专业课满足度、专业课掌握度、公共课帮助度，并计算课程总体评价。",
    annotations=ToolAnnotations(
        title="各院系毕业生对所学课程的评价（多指标分析）数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_college_course_evaluation(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    各院系毕业生对所学课程的评价（多指标分析）数据获取。
    入参：项目id、问卷id集合
    分析：分别计算专业课满足度、专业课掌握度、公共课帮助度，均为单选题，按各院系展开，均为前3项占比前5项之和。最后将这三个度在相同院系下做平均值，得出该院系下的课程总体评价。
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]
        item_year = proj_info["item_year"]
        client_code = proj_info["client_code"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询三道题的id和wt_obj（专业课满足度T00000403，专业课掌握度T00000402，公共课帮助度T00000405）
        cd_template_id = questionnaire_ids[0]
        question_codes = [
            ("T00000403", "专业课满足度"),
            ("T00000402", "专业课掌握度"),
            ("T00000405", "公共课帮助度")
        ]
        question_ids = {}
        option_map = {}
        for wt_code, label in question_codes:
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError(f"未找到{label}问题id")
            question_ids[wt_code] = q_info["id"]
            # 只需一次解析选项
            if not option_map and q_info.get("wt_obj"):
                import json
                try:
                    wt_obj_json = json.loads(q_info["wt_obj"])
                    item_list = wt_obj_json.get("itemList", [])
                    # 只保留key为1-5的选项
                    option_map = {item["key"]: item["val"] for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item}
                except Exception as e:
                    logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                    raise ToolError("问题选项描述解析失败")

        # 4. 解析shard_tb_key（client_code如CL0002→2）
        import re
        match = re.search(r"CL(0*)(\d+)", client_code)
        shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

        # 5. 查询答案表，按院系分组，分别计算三个度
        answer_table = f"re_dy_paper_answer_{split_tb}"
        student_table = f"dim_client_target_baseinfo_student_{item_year}"
        from collections import defaultdict
        degree_result = defaultdict(dict)  # {college: {label: ratio}}
        for idx, (wt_code, label) in enumerate(question_codes):
            wid = question_ids[wt_code]
            sql = f"""
                SELECT
                  s.college AS gwd1,
                  (IFNULL(SUM(t1.c1),0)+IFNULL(SUM(t1.c2),0)+IFNULL(SUM(t1.c3),0))/
                  (IFNULL(SUM(t1.c1),0)+IFNULL(SUM(t1.c2),0)+IFNULL(SUM(t1.c3),0)+IFNULL(SUM(t1.c4),0)+IFNULL(SUM(t1.c5),0)) as ratio
                FROM {answer_table} t1
                  JOIN {student_table} s ON t1.target_no = s.target_no
                WHERE
                  t1.cd_template_id = %s
                  AND t1.wid = %s
                  AND t1.ans_true = 1
                  AND s.shard_tb_key = %s
                  AND s.item_year = %s
                  AND s.college != ''
                GROUP BY
                  s.college
            """
            ans_list = db.fetchall(sql, (cd_template_id, wid, shard_tb_key, item_year))
            for row in ans_list:
                college = row.get("gwd1")
                ratio = row.get("ratio", 0)
                degree_result[college][label] = ratio
        # 6. 计算课程总体评价（取三项均值）
        result = []
        for college, vals in degree_result.items():
            v1 = vals.get("专业课满足度", 0)
            v2 = vals.get("专业课掌握度", 0)
            v3 = vals.get("公共课帮助度", 0)
            avg = (v1 + v2 + v3) / 3 if any([v1, v2, v3]) else 0
            result.append({
                "wdValNameList": [college],
                "val": [
                    {"key": "专业课满足度", "val": f"{v1:.4f}"},
                    {"key": "专业课掌握度", "val": f"{v2:.4f}"},
                    {"key": "公共课帮助度", "val": f"{v3:.4f}"},
                    {"key": "课程总体评价", "val": f"{avg:.4f}"}
                ]
            })
        # 7. 返回数据JSON
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "wdKeyNameList": [
                        {"wdKeyName": "院系"}
                    ],
                    "calcVals": result
                }
            ]
        }
    except Exception as e:
        logger.error(f"各院系毕业生对所学课程的评价（多指标分析）数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("各院系毕业生对所学课程的评价（多指标分析）数据获取失败")

@mcp.tool(
    name="get_graduate_training_objective_awareness_distribution",
    description="毕业生培养目标了解情况分布（表格里含选项分布、了解度、均值）数据获取，统计各院系毕业生对所学专业培养目标的了解程度分布。",
    annotations=ToolAnnotations(
        title="毕业生培养目标了解情况分布（表格里含选项分布、了解度、均值）数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_graduate_training_objective_awareness_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    毕业生培养目标了解情况分布（表格里含选项分布、了解度、均值）数据获取。
    入参：项目id、问卷id集合
    分析：分别计算同一道题的各选项占比，前三项占前5项目占比，及各个选项占比加权值出的均值，然后按院系展开，最后拼接的各院系对应的各指标值。
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀、item_year、client_code
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]
        item_year = proj_info["item_year"]
        client_code = proj_info["client_code"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id（固定问题编码T00003303）
        cd_template_id = questionnaire_ids[0]
        wt_code = 'T00003303'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.1 解析wt_obj字段，提取选项描述和权重，排除"无法评价"
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 解析shard_tb_key（client_code如CL0002→2）
        import re
        match = re.search(r"CL(0*)(\d+)", client_code)
        shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

        # 5. 查询答案表，按院系分组，分别计算各选项占比、前三项了解度、加权均值
        answer_table = f"re_dy_paper_answer_{split_tb}"
        student_table = f"dim_client_target_baseinfo_student_{item_year}"
        # 5.1 各选项占比
        sql_ratio = f"""
            SELECT
              s.college AS gwd1,
              SUM(t1.c1)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5)) AS ratio_c1,
              SUM(t1.c2)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5)) AS ratio_c2,
              SUM(t1.c3)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5)) AS ratio_c3,
              SUM(t1.c4)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5)) AS ratio_c4,
              SUM(t1.c5)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5)) AS ratio_c5
            FROM {answer_table} t1
              JOIN {student_table} s ON t1.target_no = s.target_no
            WHERE
              t1.cd_template_id = %s
              AND t1.wid = %s
              AND t1.ans_true = 1
              AND s.shard_tb_key = %s
              AND s.item_year = %s
              AND s.college != ''
            GROUP BY s.college
        """
        ratio_list = db.fetchall(sql_ratio, (cd_template_id, question_id, shard_tb_key, item_year))
        # 5.2 了解度（前三项占比）
        sql_degree = f"""
            SELECT
              s.college AS gwd1,
              (SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3))/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5)) AS ratio
            FROM {answer_table} t1
              JOIN {student_table} s ON t1.target_no = s.target_no
            WHERE
              t1.cd_template_id = %s
              AND t1.wid = %s
              AND t1.ans_true = 1
              AND s.shard_tb_key = %s
              AND s.item_year = %s
              AND s.college != ''
            GROUP BY s.college
        """
        degree_list = db.fetchall(sql_degree, (cd_template_id, question_id, shard_tb_key, item_year))
        degree_map = {row["gwd1"]: row["ratio"] for row in degree_list}
        # 5.3 均值（加权平均分）
        sql_avg = f"""
            SELECT
              s.college AS gwd1,
              (SUM(t1.c1)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5))*5 +
               SUM(t1.c2)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5))*4 +
               SUM(t1.c3)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5))*3 +
               SUM(t1.c4)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5))*2 +
               SUM(t1.c5)/(SUM(t1.c1)+SUM(t1.c2)+SUM(t1.c3)+SUM(t1.c4)+SUM(t1.c5))*1) as avg_val
            FROM {answer_table} t1
              JOIN {student_table} s ON t1.target_no = s.target_no
            WHERE
              t1.cd_template_id = %s
              AND t1.wid = %s
              AND t1.ans_true = 1
              AND s.shard_tb_key = %s
              AND s.item_year = %s
              AND s.college != ''
            GROUP BY s.college
        """
        avg_list = db.fetchall(sql_avg, (cd_template_id, question_id, shard_tb_key, item_year))
        avg_map = {row["gwd1"]: row["avg_val"] for row in avg_list}
        # 6. 组装返回数据
        result = []
        for row in ratio_list:
            college = row.get("gwd1")
            # 各选项占比
            val = []
            for idx, key in enumerate(["1","2","3","4","5"], 1):
                desc = option_map.get(key, {}).get("desc", f"选项{key}")
                ratio = row.get(f"ratio_c{idx}", 0)
                val.append({"key": desc, "val": f"{ratio:.6f}"})
            # 了解度
            degree = degree_map.get(college, 0)
            # 均值
            avg_val = avg_map.get(college, 0)
            result.append({
                "wdValNameList": [college],
                "val": val + [
                    {"key": "了解度(前三项占比)", "val": f"{degree:.6f}"},
                    {"key": "均值", "val": f"{avg_val:.4f}"}
                ]
            })
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "wdKeyNameList": [
                        {"wdKeyName": "院系"}
                    ],
                    "calcVals": result
                }
            ]
        }
    except Exception as e:
        logger.error(f"毕业生培养目标了解情况分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("毕业生培养目标了解情况分布数据获取失败")

@mcp.tool(
    name="get_course_setting_social_demand_fit",
    description="总体-对课程设置与社会需求的契合度数据获取。",
    annotations=ToolAnnotations(
        title="总体-对课程设置与社会需求的契合度数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_course_setting_social_demand_fit(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    总体-对课程设置与社会需求的契合度数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    本项目对应学生届次（2023,2022,2021）
    2023届对应项目id(0),2023届项目对应问卷id集合(0)。2022届对应项目id(0),2022届项目对应问卷id集合(0)。
    步骤内固定绑定参数：
    该指标对应问题编码(T00024731 所属问卷调研对象：GRADUATE_MIDDLE)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_MIDDLE）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_MIDDLE'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询问题id（固定问题编码T00024731）
        wt_code = 'T00024731'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.3 解析wt_obj字段，提取选项描述和权重，排除key为6的"无法评价"
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql_ans = f"""
            SELECT
                (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid=%s AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id, question_id))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"总体-对课程设置与社会需求的契合度数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("总体-对课程设置与社会需求的契合度数据获取失败")

@mcp.tool(
    name="get_course_setting_social_demand_fit_very_match",
    description="总体-对课程设置与社会需求的契合度评价-很符合指标数据获取。",
    annotations=ToolAnnotations(
        title="总体-对课程设置与社会需求的契合度评价-很符合指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_course_setting_social_demand_fit_very_match(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    总体-对课程设置与社会需求的契合度评价-很符合 数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其第1项(很符合)选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00024731 所属问卷调研对象：GRADUATE_MIDDLE)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_MIDDLE）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_MIDDLE'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询问题id（固定问题编码T00024731）
        wt_code = 'T00024731'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.3 解析wt_obj字段，提取选项1（很符合）描述和权重，排除key为6的"无法评价"
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算第一项(c1)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql_ans = f"""
            SELECT
                SUM(c1)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid=%s AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id, question_id))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"总体-对课程设置与社会需求的契合度评价-很符合 数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("总体-对课程设置与社会需求的契合度评价-很符合 数据获取失败")

@mcp.tool(
    name="get_course_setting_social_demand_fit_avg",
    description="总体-对课程设置与社会需求的契合度均值指标数据获取。",
    annotations=ToolAnnotations(
        title="总体-对课程设置与社会需求的契合度均值指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_course_setting_social_demand_fit_avg(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    总体-对课程设置与社会需求的契合度均值指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其第1-5项选择人数占全部5项人数占比与权重的加权均值。
    步骤固定绑定参数：
    该指标对应问题编码(T00024731 所属问卷调研对象：GRADUATE_MIDDLE)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_MIDDLE）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_MIDDLE'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询问题id（固定问题编码T00024731）
        wt_code = 'T00024731'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.3 解析wt_obj字段，提取选项描述和权重，排除key为6的"无法评价"
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，均值计算（基于各选项占比 * 其选项权重 最后得出的小于等于5的分值）
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql_ans = f"""
            SELECT 
                (SUM(c1)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5))*5 +
                 SUM(c2)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5))*4 +
                 SUM(c3)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5))*3 +
                 SUM(c4)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5))*2 +
                 SUM(c5)/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5))*1 ) as avg_val
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid=%s AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id, question_id))
        avg_val = ans_info["avg_val"] if ans_info and ans_info["avg_val"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{avg_val:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"总体-对课程设置与社会需求的契合度均值指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("总体-对课程设置与社会需求的契合度均值指标数据获取失败")

@mcp.tool(
    name="get_professional_knowledge_entrepreneurship_help",
    description="专业知识对创业的帮助度-占比指标数据获取。",
    annotations=ToolAnnotations(
        title="专业知识对创业的帮助度-占比指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_professional_knowledge_entrepreneurship_help(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    专业知识对创业的帮助度-占比指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00000645 所属问卷调研对象：GRADUATE_SHORT)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询问题id（固定问题编码T00000645）
        wt_code = 'T00000645'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.3 解析wt_obj字段，提取选项描述和权重，排除key为6的"无法评价"
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql_ans = f"""
            SELECT
                (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid=%s AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id, question_id))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"专业知识对创业的帮助度-占比指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("专业知识对创业的帮助度-占比指标数据获取失败")

@mcp.tool(
    name="get_alma_mater_satisfaction_overall",
    description="对母校的满意度-总体指标获取。",
    annotations=ToolAnnotations(
        title="对母校的满意度-总体指标获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_alma_mater_satisfaction_overall(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    对母校的满意度-总体指标获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00000444 所属问卷调研对象：GRADUATE_SHORT)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询问题id（固定问题编码T00000444）
        wt_code = 'T00000444'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.3 解析wt_obj字段，提取选项描述和权重，排除key为6的"无法评价"
        import json
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql_ans = f"""
            SELECT
                (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid=%s AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id, question_id))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"对母校的满意度-总体指标获取异常: {str(e)}", exc_info=True)
        raise ToolError("对母校的满意度-总体指标获取失败")

@mcp.tool(
    name="get_course_overall_satisfaction_overall",
    description="课程总体满意度-总体数据获取。",
    annotations=ToolAnnotations(
        title="课程总体满意度-总体数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_course_overall_satisfaction_overall(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    课程总体满意度-总体数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00000402 所属问卷调研对象：GRADUATE_SHORT,T00000403 所属问卷调研对象：GRADUATE_SHORT,T00000405 所属问卷调研对象：GRADUATE_SHORT)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询各问题对应id（T00000402, T00000403, T00000405）
        question_codes = ['T00000402', 'T00000403', 'T00000405']
        format_codes = ','.join(f"'{code}'" for code in question_codes)
        sql_q = f"SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id = %s and wt_code in ({format_codes}) and is_del = 0"
        q_info_list = db.fetchall(sql_q, (target_wt_id,))
        if not q_info_list or len(q_info_list) < 3:
            raise ToolError("未找到全部课程满意度相关问题id")
        question_ids = [q["id"] for q in q_info_list]
        # 3.3 解析选项描述和权重（以第一道题为例，所有题选项一致）
        import json
        try:
            wt_obj_json = json.loads(q_info_list[0]["wt_obj"])
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算所有相关题的前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        format_wids = ','.join(str(wid) for wid in question_ids)
        sql_ans = f"""
            SELECT (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid in ({format_wids}) AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id,))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"课程总体满意度-总体数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("课程总体满意度-总体数据获取失败")

@mcp.tool(
    name="get_teacher_overall_satisfaction_overall",
    description="任课教师总体满意度-总体指标数据获取。",
    annotations=ToolAnnotations(
        title="任课教师总体满意度-总体指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_teacher_overall_satisfaction_overall(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    任课教师总体满意度-总体指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00000406,T00000410,T00000411,T00000412,T00000413,T00000573 所属问卷调研对象：GRADUATE_SHORT)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询各问题对应id（T00000406,T00000410,T00000411,T00000412,T00000413,T00000573）
        question_codes = ['T00000406', 'T00000410', 'T00000411', 'T00000412', 'T00000413', 'T00000573']
        format_codes = ','.join(f"'{code}'" for code in question_codes)
        sql_q = f"SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id = %s and wt_code in ({format_codes}) and is_del = 0"
        q_info_list = db.fetchall(sql_q, (target_wt_id,))
        if not q_info_list or len(q_info_list) < 6:
            raise ToolError("未找到全部任课教师满意度相关问题id")
        question_ids = [q["id"] for q in q_info_list]
        # 3.3 解析选项描述和权重（以第一道题为例，所有题选项一致）
        import json
        try:
            wt_obj_json = json.loads(q_info_list[0]["wt_obj"])
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算所有相关题的前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        format_wids = ','.join(str(wid) for wid in question_ids)
        sql_ans = f"""
            SELECT (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid in ({format_wids}) AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id,))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"任课教师总体满意度-总体指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("任课教师总体满意度-总体指标数据获取失败")

@mcp.tool(
    name="get_study_style_overall_satisfaction_overall",
    description="学风建设总体满意度-总体指标数据获取。",
    annotations=ToolAnnotations(
        title="学风建设总体满意度-总体指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_study_style_overall_satisfaction_overall(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    学风建设总体满意度-总体指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00024633、T00022680、T00024634、T00016761、T00022681 所属问卷调研对象：GRADUATE_SHORT)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询各问题对应id（T00024633、T00022680、T00024634、T00016761、T00022681）
        question_codes = ['T00024633', 'T00022680', 'T00024634', 'T00016761', 'T00022681']
        format_codes = ','.join(f"'{code}'" for code in question_codes)
        sql_q = f"SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id = %s and wt_code in ({format_codes}) and is_del = 0"
        q_info_list = db.fetchall(sql_q, (target_wt_id,))
        if not q_info_list or len(q_info_list) < 5:
            raise ToolError("未找到全部学风建设满意度相关问题id")
        question_ids = [q["id"] for q in q_info_list]
        # 3.3 解析选项描述和权重（以第一道题为例，所有题选项一致）
        import json
        try:
            wt_obj_json = json.loads(q_info_list[0]["wt_obj"])
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算所有相关题的前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        format_wids = ','.join(str(wid) for wid in question_ids)
        sql_ans = f"""
            SELECT (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid in ({format_wids}) AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id,))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"学风建设总体满意度-总体指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("学风建设总体满意度-总体指标数据获取失败")

@mcp.tool(
    name="get_classroom_teaching_overall_satisfaction_overall",
    description="课堂教学总体满意度-总体指标数据获取。",
    annotations=ToolAnnotations(
        title="课堂教学总体满意度-总体指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_classroom_teaching_overall_satisfaction_overall(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    课堂教学总体满意度-总体指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00000573,T00000410,T00000411,T00000412,T00000413 所属问卷调研对象：GRADUATE_SHORT)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询各问题对应id（T00000573,T00000410,T00000411,T00000412,T00000413）
        question_codes = ['T00000573', 'T00000410', 'T00000411', 'T00000412', 'T00000413']
        format_codes = ','.join(f"'{code}'" for code in question_codes)
        sql_q = f"SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id = %s and wt_code in ({format_codes}) and is_del = 0"
        q_info_list = db.fetchall(sql_q, (target_wt_id,))
        if not q_info_list or len(q_info_list) < 5:
            raise ToolError("未找到全部课堂教学满意度相关问题id")
        question_ids = [q["id"] for q in q_info_list]
        # 3.3 解析选项描述和权重（以第一道题为例，所有题选项一致）
        import json
        try:
            wt_obj_json = json.loads(q_info_list[0]["wt_obj"])
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算所有相关题的前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        format_wids = ','.join(str(wid) for wid in question_ids)
        sql_ans = f"""
            SELECT (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid in ({format_wids}) AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id,))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"课堂教学总体满意度-总体指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("课堂教学总体满意度-总体指标数据获取失败")

@mcp.tool(
    name="get_practical_teaching_overall_satisfaction_overall",
    description="实践教学总体满意度-总体指标数据获取。",
    annotations=ToolAnnotations(
        title="实践教学总体满意度-总体指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_practical_teaching_overall_satisfaction_overall(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    实践教学总体满意度-总体指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其前3项选择人数占全部5项人数占比。
    步骤固定绑定参数：
    该指标对应问题编码(T00001296 所属问卷调研对象：GRADUATE_SHORT,T00000638 所属问卷调研对象：GRADUATE_SHORT,T00000416 所属问卷调研对象：GRADUATE_SHORT)
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3. 查询该指标对应问题的id
        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询各问题对应id（T00001296, T00000638, T00000416）
        question_codes = ['T00001296', 'T00000638', 'T00000416']
        format_codes = ','.join(f"'{code}'" for code in question_codes)
        sql_q = f"SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id = %s and wt_code in ({format_codes}) and is_del = 0"
        q_info_list = db.fetchall(sql_q, (target_wt_id,))
        if not q_info_list or len(q_info_list) < 3:
            raise ToolError("未找到全部实践教学满意度相关问题id")
        question_ids = [q["id"] for q in q_info_list]
        # 3.3 解析选项描述和权重（以第一道题为例，所有题选项一致）
        import json
        try:
            wt_obj_json = json.loads(q_info_list[0]["wt_obj"])
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，计算所有相关题的前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        format_wids = ','.join(str(wid) for wid in question_ids)
        sql_ans = f"""
            SELECT (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
            FROM {answer_table}
            WHERE cd_template_id=%s AND wid in ({format_wids}) AND ans_true=1
        """
        ans_info = db.fetchone(sql_ans, (target_wt_id,))
        ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0

        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"实践教学总体满意度-总体指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("实践教学总体满意度-总体指标数据获取失败")

@mcp.tool(
    name="get_school_employment_service_min_satisfaction",
    description="对学校就业教育/服务的评价-总体满意度最低 指标数据获取。",
    annotations=ToolAnnotations(
        title="对学校就业教育/服务的评价-总体满意度最低 指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_school_employment_service_min_satisfaction(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    对学校就业教育/服务的评价-总体满意度最低 指标数据获取。
    入参：项目id、问卷id集合
    统计多道题（子指标）的前三项占比，返回最低值。
    绑定题目：
    T00000424 生涯规划/就业指导课
    T00024641 职业规划大赛指导
    T00000425 职业咨询与辅导
    T00024639 招聘信息发布
    T00000426 校园招聘会/宣讲会
    T00000428 就业帮扶与推荐
    T00024640 就业手续办理
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询各问题对应id和选项描述
        question_codes = [
            ('T00000424', '生涯规划/就业指导课'),
            ('T00024641', '职业规划大赛指导'),
            ('T00000425', '职业咨询与辅导'),
            ('T00024639', '招聘信息发布'),
            ('T00000426', '校园招聘会/宣讲会'),
            ('T00000428', '就业帮扶与推荐'),
            ('T00024640', '就业手续办理')
        ]
        format_codes = ','.join(f"'{code}'" for code, _ in question_codes)
        sql_q = f"SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id = %s and wt_code in ({format_codes}) and is_del = 0"
        q_info_list = db.fetchall(sql_q, (target_wt_id,))
        if not q_info_list or len(q_info_list) < len(question_codes):
            raise ToolError("未找到全部就业服务满意度相关问题id")
        # 解析题目id和选项
        question_id_map = {q['wt_code']: q['id'] for q in q_info_list}
        # 解析选项（以第一道题为例，所有题选项一致）
        import json
        try:
            wt_obj_json = json.loads(q_info_list[0]["wt_obj"])
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，分别计算每个子指标的前三项占比
        answer_table = f"re_dy_paper_answer_{split_tb}"
        min_ratio = None
        for wt_code, label in question_codes:
            wid = question_id_map.get(wt_code)
            if not wid:
                continue
            sql_ans = f"""
                SELECT (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
                FROM {answer_table}
                WHERE cd_template_id=%s AND wid=%s AND ans_true=1
            """
            ans_info = db.fetchone(sql_ans, (target_wt_id, wid))
            ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0
            if min_ratio is None or ratio < min_ratio:
                min_ratio = ratio
        # 5. 组装返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{min_ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"对学校就业教育/服务的评价-总体满意度最低 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("对学校就业教育/服务的评价-总体满意度最低 指标数据获取失败")

@mcp.tool(
    name="get_school_employment_service_top3_satisfaction",
    description="对学校就业教育/服务的评价-总体满意度前3 指标数据获取。",
    annotations=ToolAnnotations(
        title="对学校就业教育/服务的评价-总体满意度前3 指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_school_employment_service_top3_satisfaction(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    对学校就业教育/服务的评价-总体满意度前3 指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标对应多道题，每道题对应一个子指标项（题干），分别计算每道题的前三项占前五项占比，得到每道题（子指标）的占比值，最终取最高的前3个子指标项描述排序，并按照'子指标项目x、子指标项目y和子指标项目z'这样的行文返回。
    绑定参数：
    该指标对应问题编码(
    T00000424 所属问卷调研对象：GRADUATE_SHORT 对应子指标描述：生涯规划/就业指导课,
    T00024641 所属问卷调研对象：GRADUATE_SHORT 对应子指标描述：职业规划大赛指导,
    T00000425 所属问卷调研对象：GRADUATE_SHORT 对应子指标描述：职业咨询与辅导,
    T00024639 所属问卷调研对象：GRADUATE_SHORT 对应子指标描述：招聘信息发布,
    T00000426 所属问卷调研对象：GRADUATE_SHORT 对应子指标描述：校园招聘会/宣讲会
    T00000428 所属问卷调研对象：GRADUATE_SHORT 对应子指标描述：就业帮扶与推荐,
    T00024640 所属问卷调研对象：GRADUATE_SHORT 对应子指标描述：就业手续办理
    )
    """
    try:
        # 1. 查询项目配置信息，及答案分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target='GRADUATE_SHORT'"
        target_wt = db.fetchone(sql_target)
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询各问题对应id和选项描述
        question_codes = [
            ('T00000424', '生涯规划/就业指导课'),
            ('T00024641', '职业规划大赛指导'),
            ('T00000425', '职业咨询与辅导'),
            ('T00024639', '招聘信息发布'),
            ('T00000426', '校园招聘会/宣讲会'),
            ('T00000428', '就业帮扶与推荐'),
            ('T00024640', '就业手续办理')
        ]
        format_codes = ','.join(f"'{code}'" for code, _ in question_codes)
        sql_q = f"SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id = %s and wt_code in ({format_codes}) and is_del = 0"
        q_info_list = db.fetchall(sql_q, (target_wt_id,))
        if not q_info_list or len(q_info_list) < len(question_codes):
            raise ToolError("未找到全部就业服务满意度相关问题id")
        # 解析题目id和选项
        question_id_map = {q['wt_code']: q['id'] for q in q_info_list}
        # 解析选项（以第一道题为例，所有题选项一致）
        import json
        try:
            wt_obj_json = json.loads(q_info_list[0]["wt_obj"])
            item_list = wt_obj_json.get("itemList", [])
            # 只保留key为1-5的选项
            option_map = {item["key"]: {"desc": item["val"], "weight": float(item["weight"])} for item in item_list if item.get("key") in ["1","2","3","4","5"] and "val" in item and "weight" in item}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，分别计算每一个子指标项对应题目的前三项(c1,c2,c3)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb}"
        ratio_list = []
        for wt_code, label in question_codes:
            wid = question_id_map.get(wt_code)
            if not wid:
                continue
            sql_ans = f"""
                SELECT (SUM(c1)+SUM(c2)+SUM(c3))/(SUM(c1)+SUM(c2)+SUM(c3)+SUM(c4)+SUM(c5)) as ratio_sum
                FROM {answer_table}
                WHERE cd_template_id=%s AND wid=%s AND ans_true=1
            """
            ans_info = db.fetchone(sql_ans, (target_wt_id, wid))
            ratio = ans_info["ratio_sum"] if ans_info and ans_info["ratio_sum"] is not None else 0
            ratio_list.append((label, ratio))
        # 4.2 选取最高的前3个子指标项描述排序
        top3 = sorted(ratio_list, key=lambda x: x[1], reverse=True)[:3]
        top3_labels = [x[0] for x in top3]
        if len(top3_labels) == 3:
            result_str = f"{top3_labels[0]}、{top3_labels[1]}和{top3_labels[2]}"
        else:
            result_str = '、'.join(top3_labels)
        # 5. 返回数据
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": result_str}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"对学校就业教育/服务的评价-总体满意度前3 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("对学校就业教育/服务的评价-总体满意度前3 指标数据获取失败")

@mcp.tool(
    name="get_employment_opportunity_more_ratio",
    description="就业机会-较多占比 指标数据获取。",
    annotations=ToolAnnotations(
        title="就业机会-较多占比 指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_employment_opportunity_more_ratio(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    就业机会-较多占比 指标数据获取。
    入参：项目id、问卷id集合
    
    """
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息，筛选GRADUATE_SHORT
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        graduate_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
        if not graduate_short_ids:
            raise ToolError("未找到GRADUATE_SHORT类型的问卷")

        # 3. 查询问题id及选项描述
        question_id = None
        wt_obj = None
        for qid in graduate_short_ids:
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code='T00000373' and is_del=0"
            q_info = db.fetchone(sql_q, (qid,))
            if q_info and q_info.get("id"):
                question_id = q_info["id"]
                wt_obj = q_info["wt_obj"]
                break
        if not question_id:
            raise ToolError("未找到对应问题id")

        # 3.3 解析wt_obj字段，提取选项描述（如有需要，可返回给业务）
        # import json
        # wt_obj_json = json.loads(wt_obj)
        # item_list = wt_obj_json.get("itemList", [])
        # ... 可根据需要返回选项描述 ...

        # 4. 查询答案表，计算前两项(c1,c2)占比前五项(c1,c2,c3,c4,c5)
        cd_template_id = graduate_short_ids[0]
       
        wid = question_id
        answer_table = f"re_dy_paper_answer_{split_tb}"
        sql = (
            f"SELECT (IFNULL(sum(c1),0)+IFNULL(sum(c2),0))/(IFNULL(sum(c1),0)+IFNULL(sum(c2),0)+IFNULL(sum(c3),0)+IFNULL(sum(c4),0)+IFNULL(sum(c5),0)) as ratio_sum "
            f"from {answer_table} WHERE cd_template_id = {cd_template_id} and wid = {wid} and ans_true=1;"
        )
        ratio_row = db.fetchone(sql)
        ratio = ratio_row["ratio_sum"] if ratio_row and ratio_row["ratio_sum"] is not None else 0
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"就业机会-较多占比 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("就业机会-较多占比 指标数据获取失败")

@mcp.tool(
    name="get_employment_opportunity_less_ratio",
    description="就业机会-较少占比 指标数据获取。",
    annotations=ToolAnnotations(
        title="就业机会-较少占比 指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_employment_opportunity_less_ratio(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    就业机会-较少占比 指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标计算该项目对应问题其后两项(第4和第5项)选择人数占全部5项人数占比。
    固定参数：
      - 问题编码：T00000373
      - 调研对象：GRADUATE_SHORT
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象），筛选GRADUATE_SHORT类型问卷
    3. 查询该指标对应问题的id（T00000373）
    4. 查询答案表，计算后两项(c4,c5)占比前五项(c1,c2,c3,c4,c5)的值
    5. 返回数据JSON
    """
    try:
        # 固定参数
        QUESTION_CODE = "T00000373"
        DY_TARGET = "GRADUATE_SHORT"

        # 1. 查询项目配置信息，获取答案表后缀
        project_conf = db.fetchone(
            f"SELECT client_code,item_year,dy_target_items,split_tb_paper from client_item WHERE id= {project_id};"
        )
        if not project_conf:
            raise ToolError("未找到项目配置信息")
        split_tb_paper = project_conf["split_tb_paper"]

        # 2. 查询问卷信息，筛选GRADUATE_SHORT类型问卷
        q_ids = ','.join(str(qid) for qid in questionnaire_ids)
        questionnaire_info = db.fetchall(
            f"SELECT id,dy_target from wt_template_customer WHERE id in ({q_ids});"
        )
        graduate_short_ids = [q["id"] for q in questionnaire_info if q["dy_target"] == DY_TARGET]
        if not graduate_short_ids:
            raise ToolError("未找到GRADUATE_SHORT类型的问卷")

        # 3. 查询问题id（T00000373）
        question_id = None
        cd_template_id = None
        for qid in graduate_short_ids:
            question = db.fetchone(
                f"SELECT id,wt_code,wt_obj from wt_template_question_customer WHERE cd_template_id = {qid} and wt_code = '{QUESTION_CODE}' and is_del = 0;"
            )
            if question:
                question_id = question["id"]
                cd_template_id = qid
                break
        if not question_id:
            raise ToolError("未找到对应问题id")

        # 4. 查询答案表，统计后两项(c4,c5)占比前五项(c1,c2,c3,c4,c5)
        answer_table = f"re_dy_paper_answer_{split_tb_paper}"
        # 这里假设所有问卷的wid都需要统计，实际业务如有wid筛选可补充
        # 统计所有问卷id的结果合并
        ratio_sum = 0
        total_count = 0
        for qid in graduate_short_ids:
            sql = (
                f"SELECT sum(c4) as c4, sum(c5) as c5, sum(c1) as c1, sum(c2) as c2, sum(c3) as c3 "
                f"FROM {answer_table} WHERE cd_template_id = {qid} and wid = {question_id} and ans_true=1;"
            )
            row = db.fetchone(sql)
            if row:
                c1 = row.get("c1") or 0
                c2 = row.get("c2") or 0
                c3 = row.get("c3") or 0
                c4 = row.get("c4") or 0
                c5 = row.get("c5") or 0
                total = c1 + c2 + c3 + c4 + c5
                if total > 0:
                    ratio_sum += (c4 + c5)
                    total_count += total
        ratio = (ratio_sum / total_count) if total_count > 0 else 0
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"就业机会-较少占比 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("就业机会-较少占比 指标数据获取失败")

@mcp.tool(
    name="get_school_admission_advice_top2_suggestion",
    description="对学校招生工作的建议-总体前2 指标数据获取。以'子指标项目x、子指标项目y'格式返回",
    annotations=ToolAnnotations(
        title="对学校招生工作的建议-总体前2 指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_school_admission_advice_top2_suggestion(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    对学校招生工作的建议-总体前2 指标数据获取。
    入参：项目id、问卷id集合
    分析：
    - 此指标对应1道多选题（T00003310，调研对象：GRADUATE_SHORT），每个选项为一个子指标项。
    - 统计每个选项人数占所有答题人数的占比，得到每个子指标的占比值。
    - 最终取占比最高的前2个子指标项描述排序，并以'子指标项目x、子指标项目y'格式返回。
    绑定参数：
    - 问题编码：T00003310
    - 所属问卷调研对象：GRADUATE_SHORT
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀。
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）。
    3. 查询该指标对应问题的id。
    4. 查询答案表，统计每个选项的占比。
    5. 选取占比最高的前2项，拼接描述返回。
    6. 返回数据JSON，格式见下。
    """
    import json
    try:
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息，筛选GRADUATE_SHORT类型问卷
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        grad_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
        if not grad_short_ids:
            raise ToolError("未找到GRADUATE_SHORT类型问卷")

        # 3. 查询该指标对应问题的id和选项描述（只取第一个GRADUATE_SHORT问卷）
        cd_template_id = grad_short_ids[0]
        wt_code = 'T00003310'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")
        wt_obj_json = json.loads(wt_obj)
        item_list = wt_obj_json.get("itemList", [])
        # 过滤掉"无法评价"选项（如有）
        option_map = {item["key"]: item["val"] for item in item_list if "无法评价" not in item["val"]}
        if not option_map:
            raise ToolError("未找到有效选项描述")

        # 4. 查询答案表，统计每个选项的占比
        answer_table = f"re_dy_paper_answer_{split_tb}"
        select_fields = []
        for k, v in option_map.items():
            select_fields.append(f"IFNULL(sum(c{k}),0)/count(*) as `{v}`")
        select_sql = f"SELECT {', '.join(select_fields)} FROM {answer_table} WHERE cd_template_id=%s and wid=%s and ans_true=1"
        ans_info = db.fetchone(select_sql, (cd_template_id, question_id))
        if not ans_info:
            raise ToolError("未查到答案数据")
        # 5. 选取占比最高的前2项
        option_ratios = {k: float(ans_info.get(k, 0)) for k in ans_info.keys()}
        top2 = sorted(option_ratios.items(), key=lambda x: x[1], reverse=True)[:2]
        top2_desc = [item[0] for item in top2]
        result_str = "、".join(top2_desc)
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": result_str}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"对学校招生工作的建议-总体前2 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("对学校招生工作的建议-总体前2 指标数据获取失败")

@mcp.tool(
    name="get_school_talent_training_advice_top2_suggestion",
    description="对学校人才培养工作的建议-总体前2 指标数据获取。",
    annotations=ToolAnnotations(
        title="对学校人才培养工作的建议-总体前2 指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_school_talent_training_advice_top2_suggestion(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")]
) -> dict:
    """
    对学校人才培养工作的建议-总体前2 指标数据获取。
    入参：项目id、问卷id集合
    分析：此指标对应1道多选题，这道题的每一个选项为一个子指标项，统计每一个选项人数占所有答了这道题的人数的占比。这样就得到了每一个子指标的一个占比值，最终这个指标值取最高的前2个子指标项描述排序，并按照'子指标项目x、子指标项目y'这样的行文返回。
    绑定参数：
    该指标对应问题编码(T00003311 所属问卷调研对象：GRADUATE_SHORT )
    """
    try:
        import json
        # 1. 查询项目配置信息，获取分表后缀
        sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("split_tb_paper"):
            raise ToolError("未找到项目配置信息或分表后缀")
        split_tb = proj_info["split_tb_paper"]

        # 2. 查询问卷信息（问卷id,对应调研对象）
        format_ids = ','.join(str(qid) for qid in questionnaire_ids)
        sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
        wt_info = db.fetchall(sql_wt)
        if not wt_info:
            raise ToolError("未找到问卷信息")

        # 3.1 确定该问题对应问卷id,根据问题所属调研对象（GRADUATE_SHORT）
        dy_target = "GRADUATE_SHORT"
        sql_target = f"SELECT id FROM wt_template_customer WHERE id in ({format_ids}) and dy_target=%s"
        target_wt = db.fetchone(sql_target, (dy_target,))
        if not target_wt or not target_wt.get("id"):
            raise ToolError("未找到指定调研对象的问卷id")
        target_wt_id = target_wt["id"]

        # 3.2 查询问题id（固定问题编码T00003311）
        wt_code = 'T00003311'
        sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
        q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
        if not q_info or not q_info.get("id"):
            raise ToolError("未找到对应问题id")
        question_id = q_info["id"]
        wt_obj = q_info.get("wt_obj")
        if not wt_obj:
            raise ToolError("未找到问题选项描述")

        # 3.3 解析wt_obj字段，提取选项key与描述val，排除"无法评价"
        try:
            wt_obj_json = json.loads(wt_obj)
            item_list = wt_obj_json.get("itemList", [])
            option_map = {item["key"]: item["val"] for item in item_list if item["val"] != "无法评价"}
        except Exception as e:
            logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
            raise ToolError("问题选项描述解析失败")

        # 4. 查询答案表，统计每个子指标项占比
        answer_table = f"re_dy_paper_answer_{split_tb}"
        # 构造sum(c1)/count(*), sum(c2)/count(*), ...
        sum_exprs = []
        for i in range(1, len(option_map) + 1):
            sum_exprs.append(f"IFNULL(sum(c{i}),0)/count(*) as '{option_map.get(str(i), str(i))}'")
        sum_sql = ", ".join(sum_exprs)
        sql_ans = f"SELECT {sum_sql} FROM {answer_table} WHERE cd_template_id=%s and wid=%s and ans_true=1"
        ans_info = db.fetchone(sql_ans, (target_wt_id, question_id))
        if not ans_info:
            raise ToolError("未查到答案数据")

        # 5. 选取占比最高的前2项
        option_ratios = {k: float(ans_info.get(k, 0)) for k in ans_info.keys()}
        top2 = sorted(option_ratios.items(), key=lambda x: x[1], reverse=True)[:2]
        top2_desc = [item[0] for item in top2]
        result_str = "、".join(top2_desc)

        # 6. 返回数据JSON
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": result_str}
                            ]
                        }
                    ]
                }
            ]
        }
    except Exception as e:
        logger.error(f"对学校人才培养工作的建议-总体前2 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("对学校人才培养工作的建议-总体前2 指标数据获取失败")

@mcp.tool(
    name="get_single_education_employment_area_top1",
    description="单学历-就业地区第1名指标数据获取，根据项目id、问卷id集合、产品id，统计就业省份分布并返回占比最高的省份及其值。",
    annotations=ToolAnnotations(
        title="单学历-就业地区第1名指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_single_education_employment_area_top1(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    单学历-就业地区第1名 指标数据获取。
    入参：项目id、问卷id集合、学生届次、产品id
    分析：此题是个编码题,选择的是省市区，此指标旨在将选择的省份聚合，算出每个省份对应答题人占比，最后取占比最高的省份及其值。
    固定参数：
      - 问题编码：T00000370
      - 调研对象：GRADUATE_SHORT
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
    3. 查询该指标对应问题的id
    4. 查询答案表，获取此题答案详情，统计第一级各省份的数量及占比
    5. 返回数据JSON
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]

            # 2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)

            # 3.1 确定该问题对应问卷id,根据问题所属调研对象
            graduate_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
            if not graduate_short_ids:
                raise ToolError("未找到GRADUATE_SHORT类型的问卷")
            target_wt_id = graduate_short_ids[0]

            # 3.2 查询问题id
            wt_code = 'T00000370'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.3 解析wt_obj字段，了解问题结构
            try:
                wt_obj_json = json.loads(wt_obj)
                # 此题type为CODE编码题，其关联关系为relationData字段，分别为3级 就业省份2023，就业市2023，就业区县2023
                # 此指标为获取第一级（省份）的分布占比
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 查询答案表，获取此题答案详情
            answer_table = f"re_dy_paper_answer_{split_tb}"
            sql_ans = f"SELECT answer_obj FROM {answer_table} WHERE cd_template_id=%s AND wid=%s AND ans_true=1"
            ans_rows = db.fetchall(sql_ans, (target_wt_id, question_id))
            if not ans_rows:
                raise ToolError("未查到答案数据")

            # 统计第一级各省份的数量
            province_counts = {}
            total_count = 0

            for row in ans_rows:
                answer_obj = row.get("answer_obj")
                if not answer_obj:
                    continue

                try:
                    # 解析答案对象，格式如：[{"wk":"","wv":"安徽省","val":"","sort":null},{"wk":"","wv":"安庆市","val":"","sort":null},{"wk":"","wv":"宿松县","val":"","sort":null}]
                    answer_list = json.loads(answer_obj)
                    if answer_list and len(answer_list) > 0:
                        # 获取第一级（省份）
                        province = answer_list[0].get("wv")
                        if province:
                            province_counts[province] = province_counts.get(province, 0) + 1
                            total_count += 1
                except Exception as e:
                    logger.error(f"答案解析失败: {str(e)}", exc_info=True)
                    continue

            # 计算各省份占比，找出占比最高的省份
            if total_count == 0:
                raise ToolError("有效答案数量为0")

            province_ratios = {province: count / total_count for province, count in province_counts.items()}
            top_province = max(province_ratios.items(), key=lambda x: x[1])
            top_province_name = top_province[0]
            top_province_ratio = top_province[1]

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "wdValNameList": [],
                                "val": [
                                    {"key": top_province_name, "val": f"{top_province_ratio:.4f}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            # 默认实现，与PD0038相同
            return calc_for_pd0038()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
    except Exception as e:
        logger.error(f"单学历-就业地区第1名 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("单学历-就业地区第1名 指标数据获取失败")

@mcp.tool(
    name="get_single_education_employment_industry_top1",
    description="单学历-就业行业第1名指标数据获取，根据项目id、问卷id集合、产品id，统计就业行业分布并返回占比最高的行业及其值。",
    annotations=ToolAnnotations(
        title="单学历-就业行业第1名指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_single_education_employment_industry_top1(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    单学历-就业行业第1名 指标数据获取。
    入参：项目id、问卷id集合、产品id
    分析：此题是个编码题,选择的是三级行业，此指标旨在将选择的第一级行业聚合，算出每个行业对应答题人占比，最后取占比最高的行业及其值。
    固定参数：
      - 问题编码：T00000371
      - 调研对象：GRADUATE_SHORT
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
    3. 查询该指标对应问题的id
    4. 查询答案表，获取此题答案详情，统计第一级各行业的数量及占比
    5. 返回数据JSON
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]

            # 2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)

            # 3.1 确定该问题对应问卷id,根据问题所属调研对象
            graduate_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
            if not graduate_short_ids:
                raise ToolError("未找到GRADUATE_SHORT类型的问卷")
            target_wt_id = graduate_short_ids[0]

            # 3.2 查询问题id
            wt_code = 'T00000371'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (target_wt_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")

            # 3.3 解析wt_obj字段，了解问题结构
            try:
                wt_obj_json = json.loads(wt_obj)
                # 此题type为CODE编码题，其关联关系为relationData字段，分别为2级 行业门类，行业大类
                # 此指标为获取第一级（行业门类）的分布占比
            except Exception as e:
                logger.error(f"wt_obj字段解析失败: {str(e)}", exc_info=True)
                raise ToolError("问题选项描述解析失败")

            # 4. 查询答案表，获取此题答案详情
            answer_table = f"re_dy_paper_answer_{split_tb}"
            sql_ans = f"SELECT answer_obj FROM {answer_table} WHERE cd_template_id=%s AND wid=%s AND ans_true=1"
            ans_rows = db.fetchall(sql_ans, (target_wt_id, question_id))
            if not ans_rows:
                raise ToolError("未查到答案数据")

            # 统计第一级各行业的数量
            industry_counts = {}
            total_count = 0

            for row in ans_rows:
                answer_obj = row.get("answer_obj")
                if not answer_obj:
                    continue

                try:
                    # 解析答案对象，格式如：[{"wk":"","wv":"信息传输、软件和信息技术服务业","val":"","sort":null},{"wk":"","wv":"软件和信息技术服务业","val":"","sort":null}]
                    answer_list = json.loads(answer_obj)
                    if answer_list and len(answer_list) > 0:
                        # 获取第一级（行业门类）
                        industry = answer_list[0].get("wv")
                        if industry:
                            industry_counts[industry] = industry_counts.get(industry, 0) + 1
                            total_count += 1
                except Exception as e:
                    logger.error(f"答案解析失败: {str(e)}", exc_info=True)
                    continue

            # 计算各行业占比，找出占比最高的行业
            if total_count == 0:
                raise ToolError("有效答案数量为0")

            industry_ratios = {industry: count / total_count for industry, count in industry_counts.items()}
            top_industry = max(industry_ratios.items(), key=lambda x: x[1])
            top_industry_name = top_industry[0]
            top_industry_ratio = top_industry[1]

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "wdValNameList": [],
                                "val": [
                                    {"key": top_industry_name, "val": f"{top_industry_ratio:.4f}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            # 默认实现，与PD0038相同
            return calc_for_pd0038()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
    except Exception as e:
        logger.error(f"单学历-就业行业第1名 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("单学历-就业行业第1名 指标数据获取失败")

@mcp.tool(
    name="get_single_education_employment_unit_top1",
    description="单学历-就业单位第1名指标数据获取，根据项目id、问卷id集合、产品id，统计就业单位性质分布并返回占比最高的单位性质及其值。",
    annotations=ToolAnnotations(
        title="单学历-就业单位第1名指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_single_education_employment_unit_top1(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    单学历-就业单位第1名指标数据获取。
    入参：项目id、问卷id集合、产品id
    分析：此题是个单选题，此指标旨计算每个选项的占比，比最终选出最大占比值的选项及其占比值。
    绑定参数：
      - 问题编码：T00000367
      - 调研对象：GRADUATE_SHORT
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
    3. 查询该指标对应问题的id
    4. 查询答案表，计算每个选项的人数占比，并根据题目配置，赋予每个选项对应描述
    5. 返回数据JSON
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]

            # 2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)

            # 3.1 确定该问题对应问卷id,根据问题所属调研对象
            graduate_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
            if not graduate_short_ids:
                raise ToolError("未找到GRADUATE_SHORT类型的问卷")
            
            # 3.2 查询问题id
            cd_template_id = graduate_short_ids[0]
            wt_code = 'T00000367'
            sql_q = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            q_info = db.fetchone(sql_q, (cd_template_id, wt_code))
            if not q_info or not q_info.get("id"):
                raise ToolError("未找到对应问题id")
            question_id = q_info["id"]
            wt_obj = q_info.get("wt_obj")
            if not wt_obj:
                raise ToolError("未找到问题选项描述")
            
            # 3.3 解析wt_obj字段，提取选项描述
            import json
            try:
                wt_obj_json = json.loads(wt_obj)
                item_list = wt_obj_json.get("itemList", [])
                if not item_list:
                    raise ToolError("未找到问题选项列表")
                
                # 构建选项key到选项描述的映射
                option_map = {item["key"]: item["val"] for item in item_list}
                
            except json.JSONDecodeError:
                raise ToolError("解析问题选项描述失败")
            
            # 4. 查询答案表，计算每个选项的人数占比
            answer_table = f"re_dy_paper_answer_{split_tb}"
            
            # 构建SQL查询，计算每个选项的占比
            sql_columns = []
            for i in range(1, 15):  # 根据题目有14个选项
                sql_columns.append(f"sum(c{i})/count(*) as 'c{i}'")
            
            sql_ans = f"SELECT {', '.join(sql_columns)} FROM {answer_table} WHERE cd_template_id=%s AND wid=%s AND ans_true=1"
            ans_info = db.fetchone(sql_ans, (cd_template_id, question_id))
            
            if not ans_info:
                raise ToolError("未找到答案数据")
            
            # 4.1 找出占比最大的选项
            max_option = None
            max_value = -1
            
            for i in range(1, 15):
                col_name = f"c{i}"
                if col_name in ans_info and ans_info[col_name] is not None:
                    value = float(ans_info[col_name])
                    if value > max_value:
                        max_value = value
                        max_option = str(i)
            
            if max_option is None:
                raise ToolError("未找到有效的选项占比数据")
            
            # 获取最大占比选项的描述
            max_option_desc = option_map.get(max_option, f"选项{max_option}")
            
            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "wdValNameList": [
                                    max_option_desc
                                ],
                                "val": [
                                    {"key": "default", "val": f"{max_value:.4f}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0038()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
    except Exception as e:
        logger.error(f"单学历-就业单位第1名 指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("单学历-就业单位第1名 指标数据获取失败")

@mcp.tool(
    name="get_single_education_employment_field_highest_income",
    description="单学历-不同就业领域毕业生就业质量差异分析-收入最高指标数据获取，根据项目id、问卷id集合、产品id，统计各行业平均薪酬并返回收入最高的行业及其值。",
    annotations=ToolAnnotations(
        title="单学历-不同就业领域毕业生就业质量差异分析-收入最高指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_single_education_employment_field_highest_income(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    单学历-不同就业领域毕业生就业质量差异分析-收入最高指标数据获取。
    入参：项目id、问卷id集合、产品id
    分析：此指标需要结合两道题，结合T00000374题的平均薪酬，和答了T00000371这道题的人的一级行业，
         然后按一级行业分布，取平均薪酬，来反应各一级行业平均薪酬分布，最后取薪酬排名第一的行业及其平均值。
         此外此指标限定，答题人数满足15人及以上的行业才会被统计。
    绑定参数：
      - 问题编码：行业编码题(T00000371)和薪酬数字填空题(T00000374)
      - 调研对象：GRADUATE_SHORT
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
    3. 查询该指标对应问题的id
    4. 查询答案表，关联两道题的答案，按行业分组计算平均薪酬，筛选出人数>=15的行业，并按平均薪酬降序排序
    5. 返回数据JSON
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]
            
            # 生成shard_tb_key，用于查询优化
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = f"{match.group(2)}_{item_year}" if match else f"{client_code}_{item_year}"

            # 2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)

            # 3.1 确定该问题对应问卷id,根据问题所属调研对象
            graduate_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
            if not graduate_short_ids:
                raise ToolError("未找到GRADUATE_SHORT类型的问卷")
            target_wt_id = graduate_short_ids[0]

            # 3.2.1 查询行业编码题的问题id
            industry_code = 'T00000371'
            sql_industry = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            industry_info = db.fetchone(sql_industry, (target_wt_id, industry_code))
            if not industry_info or not industry_info.get("id"):
                raise ToolError("未找到行业编码题的问题id")
            industry_question_id = industry_info["id"]

            # 3.2.2 查询薪酬填空题的问题id
            salary_code = 'T00000374'
            sql_salary = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            salary_info = db.fetchone(sql_salary, (target_wt_id, salary_code))
            if not salary_info or not salary_info.get("id"):
                raise ToolError("未找到薪酬填空题的问题id")
            salary_question_id = salary_info["id"]

            # 4. 查询答案表，关联两道题的答案
            answer_table = f"re_dy_paper_answer_{split_tb}"
            
            # 4.1 查询各行业平均薪酬
            sql_ans = f"""
            SELECT 
                JSON_UNQUOTE(JSON_EXTRACT(t2.answer_obj, '$[0].wv')) AS wv, 
                ROUND(AVG(t1.n1)) AS val 
            FROM 
                {answer_table} t1 
                JOIN {answer_table} t2 ON t1.target_no = t2.target_no 
            WHERE 
                t1.shard_tb_key = %s 
                AND t1.cd_template_id = %s 
                AND t1.wid = %s 
                AND t1.ans_true = 1 
                AND t2.shard_tb_key = %s 
                AND t2.cd_template_id = %s 
                AND t2.wid = %s 
                AND t2.ans_true = 1 
            GROUP BY 
                JSON_UNQUOTE(JSON_EXTRACT(t2.answer_obj, '$[0].wv')) 
            HAVING 
                wv IN ( 
                SELECT 
                    JSON_UNQUOTE(JSON_EXTRACT(t2.answer_obj, '$[0].wv')) AS wv 
                FROM 
                    {answer_table} t2 
                WHERE 
                    t2.cd_template_id = %s 
                    AND t2.wid = %s 
                    AND t2.ans_true = 1 
                GROUP BY 
                    JSON_UNQUOTE(JSON_EXTRACT(t2.answer_obj, '$[0].wv')) 
                HAVING 
                    COUNT(*) >= 15 
                ) 
            ORDER BY 
                val DESC
            """
            
            params = (
                shard_tb_key, target_wt_id, salary_question_id,
                shard_tb_key, target_wt_id, industry_question_id,
                target_wt_id, industry_question_id
            )
            
            ans_rows = db.fetchall(sql_ans, params)
            
            if not ans_rows or len(ans_rows) == 0:
                sql_print = sql_ans % tuple(repr(p) for p in params)
                print("最终执行SQL：", sql_print)
                raise ToolError("未查询到符合条件的行业薪酬数据")
            
            # 获取薪酬最高的行业及其值
            top_industry = ans_rows[0]
            top_industry_name = top_industry["wv"]
            top_industry_salary = top_industry["val"]
            
            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [],
                        "calcVals": [
                            {
                                "wdValNameList": [
                                    top_industry_name
                                ],
                                "val": [
                                    {"key": "default", "val": str(top_industry_salary)}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            # 默认实现，与PD0038相同
            return calc_for_pd0038()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
    except Exception as e:
        logger.error(f"单学历-不同就业领域毕业生就业质量差异分析-收入最高指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("单学历-不同就业领域毕业生就业质量差异分析-收入最高指标数据获取失败")

@mcp.tool(
    name="get_single_education_employment_field_highest_satisfaction",
    description="单学历-不同就业领域毕业生就业质量差异分析-工作满意度最高指标数据获取，根据项目id、问卷id集合、产品id，统计各行业工作满意度并返回满意度最高的行业及其值。",
    annotations=ToolAnnotations(
        title="单学历-不同就业领域毕业生就业质量差异分析-工作满意度最高指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_single_education_employment_field_highest_satisfaction(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    单学历-不同就业领域毕业生就业质量差异分析-工作满意度最高指标数据获取。
    入参：项目id、问卷id集合、产品id
    分析：此指标需要结合两道题，结合T00000376题的工作满意度调查，和答了T00000371这道题的人的一级行业，
         然后按一级行业分布，取满意度调查题的前三项占比前五项的占比值，来反应各一级行业的工作满意度分布，
         最后取满意度值排名第一的行业及其值。此外此指标限定，答题人数满足15人及以上的行业才会被统计。
    绑定参数：
      - 行业编码题：T00000371（所属问卷调研对象：GRADUATE_SHORT）
      - 满意度选择题：T00000376（所属问卷调研对象：GRADUATE_SHORT）
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
    3. 查询该指标对应问题的id
    4. 查询答案表，关联两道题的答案，按行业分布统计满意度占比值，取最大值行业及其值
    5. 返回数据JSON
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper FROM client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]
            
            # 2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target FROM wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            
            # 3.1 确定该问题对应问卷id,根据问题所属调研对象
            graduate_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
            if not graduate_short_ids:
                raise ToolError("未找到GRADUATE_SHORT类型的问卷")
            
            cd_template_id = graduate_short_ids[0]
            
            # 3.2.1 查询行业编码题
            industry_code = 'T00000371'
            sql_industry = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            industry_info = db.fetchone(sql_industry, (cd_template_id, industry_code))
            if not industry_info or not industry_info.get("id"):
                raise ToolError("未找到行业编码题id")
            industry_id = industry_info["id"]
            
            # 3.2.2 查询满意度单选题
            satisfaction_code = 'T00000376'
            sql_satisfaction = "SELECT id,wt_code,wt_obj FROM wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            satisfaction_info = db.fetchone(sql_satisfaction, (cd_template_id, satisfaction_code))
            if not satisfaction_info or not satisfaction_info.get("id"):
                raise ToolError("未找到满意度单选题id")
            satisfaction_id = satisfaction_info["id"]
            
            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code
            shard_tb_key = f"{shard_tb_key}_{item_year}"
            
            # 5. 查询答案表，关联两道题的答案
            answer_table = f"re_dy_paper_answer_{split_tb}"
            sql = f"""
                SELECT 
                  JSON_UNQUOTE(
                  JSON_EXTRACT(t2.answer_obj, '$[0].wv')) AS wv, 
                  (sum(t1.c1)+sum(t1.c2)+sum(t1.c3))/(sum(t1.c1)+sum(t1.c2)+sum(t1.c3)+sum(t1.c4)+sum(t1.c5)) as val 
                FROM 
                  {answer_table} t1 
                  JOIN {answer_table} t2 ON t1.target_no = t2.target_no 
                WHERE 
                  t1.shard_tb_key = %s 
                  AND t1.cd_template_id = %s 
                  AND t1.wid = %s 
                  AND t1.ans_true = 1 
                  AND t2.shard_tb_key = %s 
                  AND t2.cd_template_id = %s 
                  AND t2.wid = %s 
                  AND t2.ans_true = 1 
                GROUP BY 
                  JSON_UNQUOTE(
                  JSON_EXTRACT(t2.answer_obj, '$[0].wv')) 
                HAVING 
                  wv IN ( 
                  SELECT 
                    JSON_UNQUOTE(
                    JSON_EXTRACT(t2.answer_obj, '$[0].wv')) AS wv 
                  FROM 
                    {answer_table} t2 
                  WHERE 
                    t2.cd_template_id = %s 
                    AND t2.wid = %s 
                    AND t2.ans_true = 1 
                  GROUP BY 
                    JSON_UNQUOTE(
                    JSON_EXTRACT(t2.answer_obj, '$[0].wv')) 
                  HAVING 
                    count(*) >= 15 
                  ) 
                ORDER BY 
                  val DESC
            """
            
            ans_list = db.fetchall(sql, (shard_tb_key, cd_template_id, satisfaction_id, 
                                         shard_tb_key, cd_template_id, industry_id,
                                         cd_template_id, industry_id))
            
            # 获取行业名称映射
            industry_mapping = {
                "A": "农、林、牧、渔业",
                "B": "采矿业",
                "C": "制造业",
                "D": "电力、热力、燃气及水生产和供应业",
                "E": "建筑业",
                "F": "批发和零售业",
                "G": "交通运输、仓储和邮政业",
                "H": "住宿和餐饮业",
                "I": "信息传输、软件和信息技术服务业",
                "J": "金融业",
                "K": "房地产业",
                "L": "租赁和商务服务业",
                "M": "科学研究和技术服务业",
                "N": "水利、环境和公共设施管理业",
                "O": "居民服务、修理和其他服务业",
                "P": "教育",
                "Q": "卫生和社会工作",
                "R": "文化、体育和娱乐业",
                "S": "公共管理、社会保障和社会组织",
                "T": "国际组织"
            }
            
            # 6. 组装返回数据
            if not ans_list:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "calcVals": []
                        }
                    ]
                }
            
            # 获取满意度最高的行业
            top_industry = ans_list[0]
            industry_code = top_industry["wv"]
            industry_name = industry_mapping.get(industry_code, industry_code)
            satisfaction_val = top_industry["val"]
            
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "wdValNameList": [
                                    industry_name
                                ],
                                "val": [
                                    {"key": "default", "val": f"{satisfaction_val:.4f}"}
                                ]
                            }
                        ]
                    }
                ]
            }
        
        def calc_for_default():
            return calc_for_pd0038()
        
        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
        
    except Exception as e:
        logger.error(f"单学历-不同就业领域毕业生就业质量差异分析-工作满意度最高指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("单学历-不同就业领域毕业生就业质量差异分析-工作满意度最高指标数据获取失败")

@mcp.tool(
    name="get_single_education_employment_field_lowest_turnover",
    description="单学历-不同就业领域毕业生就业质量差异分析-离职率最低指标数据获取，根据项目id、问卷id集合、产品ID等参数，获取数据。",
    annotations=ToolAnnotations(
        title="单学历-不同就业领域毕业生就业质量差异分析-离职率最低指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_single_education_employment_field_lowest_turnover(
    project_id: Annotated[int, Field(description="项目id")],
    questionnaire_ids: Annotated[list[int], Field(description="本项目对应问卷id集合")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    单学历-不同就业领域毕业生就业质量差异分析-离职率最低指标数据获取。
    入参：项目id、问卷id集合、产品id
    分析：此指标需要结合两道题 结合T00000385题的离职次数选择题，和答了T00000371这道题的人的一级行业，
         然后按一级行业分布，取离职次数题的2,3,4项目占比前四项的占比值，来反应各一级行业的离职率分布，
         最后取离职率值最小的行业及其值。此外此指标限定，答题人数满足15人及以上的行业才会被统计。
    绑定参数：
      - 行业编码题：T00000371（所属问卷调研对象：GRADUATE_SHORT）
      - 离职次数选择题：T00000385（所属问卷调研对象：GRADUATE_SHORT）
    步骤：
    1. 根据项目id,查询项目配置信息，及答案分表后缀
    2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
    3. 查询该指标对应问题的id
    4. 查询答案表，关联两道题的答案，按行业分组统计离职率
    5. 返回数据JSON
    """
    try:
        def calc_for_pd0038():
            # 1. 查询项目配置信息，获取分表后缀
            sql_proj = "SELECT client_code,item_year,dy_target_items,split_tb_paper from client_item WHERE id=%s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("split_tb_paper"):
                raise ToolError("未找到项目配置信息或分表后缀")
            split_tb = proj_info["split_tb_paper"]
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]
            
            # 2. 根据问卷id,查询问卷信息（问卷id,对应调研对象）
            format_ids = ','.join(str(qid) for qid in questionnaire_ids)
            sql_wt = f"SELECT id,dy_target from wt_template_customer WHERE id in ({format_ids})"
            wt_info = db.fetchall(sql_wt)
            
            # 3.1 确定该问题对应问卷id,根据问题所属调研对象
            graduate_short_ids = [row["id"] for row in wt_info if row["dy_target"] == "GRADUATE_SHORT"]
            if not graduate_short_ids:
                raise ToolError("未找到GRADUATE_SHORT类型的问卷")
            target_wt_id = graduate_short_ids[0]
            
            # 3.2.1 查询行业编码题
            industry_code = 'T00000371'
            sql_industry = "SELECT id,wt_code,wt_obj from wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            industry_info = db.fetchone(sql_industry, (target_wt_id, industry_code))
            if not industry_info or not industry_info.get("id"):
                raise ToolError("未找到行业编码题id")
            industry_id = industry_info["id"]
            
            # 3.2.2 查询离职次数单选题
            turnover_code = 'T00000385'
            sql_turnover = "SELECT id,wt_code,wt_obj from wt_template_question_customer WHERE cd_template_id=%s and wt_code=%s and is_del=0"
            turnover_info = db.fetchone(sql_turnover, (target_wt_id, turnover_code))
            if not turnover_info or not turnover_info.get("id"):
                raise ToolError("未找到离职次数单选题id")
            turnover_id = turnover_info["id"]
            
            # 4. 解析shard_tb_key（client_code如CL0002→2）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code
            shard_tb_key = f"{shard_tb_key}_{item_year}"
            
            # 4. 查询答案表，关联两道题的答案
            answer_table = f"re_dy_paper_answer_{split_tb}"
            sql = f"""
                SELECT 
                  JSON_UNQUOTE(
                  JSON_EXTRACT(t2.answer_obj, '$[0].wv')) AS wv, 
                  (sum(t1.c2)+sum(t1.c3)+sum(t1.c4))/(sum(t1.c1)+sum(t1.c2)+sum(t1.c3)+sum(t1.c4)) as val 
                FROM 
                  {answer_table} t1 
                  JOIN {answer_table} t2 ON t1.target_no = t2.target_no 
                WHERE 
                  t1.shard_tb_key = %s 
                  AND t1.cd_template_id = %s 
                  AND t1.wid = %s 
                  AND t1.ans_true = 1 
                  AND t2.shard_tb_key = %s 
                  AND t2.cd_template_id = %s 
                  AND t2.wid = %s 
                  AND t2.ans_true = 1 
                GROUP BY 
                  JSON_UNQUOTE(
                  JSON_EXTRACT(t2.answer_obj, '$[0].wv')) 
                HAVING 
                  wv IN ( 
                  SELECT 
                    JSON_UNQUOTE(
                    JSON_EXTRACT(t2.answer_obj, '$[0].wv')) AS wv 
                  FROM 
                    {answer_table} t2 
                  WHERE 
                    t2.cd_template_id = %s 
                    AND t2.wid = %s 
                    AND t2.ans_true = 1 
                  GROUP BY 
                    JSON_UNQUOTE(
                    JSON_EXTRACT(t2.answer_obj, '$[0].wv')) 
                  HAVING 
                    count(*) >= 15 
                  ) 
                ORDER BY 
                  val asc
            """
            
            ans_list = db.fetchall(sql, (shard_tb_key, target_wt_id, turnover_id, 
                                         shard_tb_key, target_wt_id, industry_id, 
                                         target_wt_id, industry_id))
            
            # 5. 处理结果，取离职率最低的行业
            if not ans_list:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "calcVals": [
                                {
                                    "val": [
                                        {"key": "default", "val": ""}
                                    ]
                                }
                            ]
                        }
                    ]
                }
            
            # 获取离职率最低的行业及其值
            lowest_turnover = ans_list[0]
            industry_name = lowest_turnover["wv"]
            turnover_rate = lowest_turnover["val"] * 100
            
            # 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"在{industry_name}就业的毕业生的离职率最低，为{turnover_rate:.2f}%"}
                                ]
                            }
                        ]
                    }
                ]
            }
        
        def calc_for_default():
            return calc_for_pd0038()
        
        # 产品ID分发逻辑
        product_logic_map = {
            "PD0038": calc_for_pd0038,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
        
    except Exception as e:
        logger.error(f"单学历-不同就业领域毕业生就业质量差异分析-离职率最低指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("单学历-不同就业领域毕业生就业质量差异分析-离职率最低指标数据获取失败")

@mcp.tool(
    name="get_objective_data_end_time",
    description="客观数据截止时间指标数据获取，根据项目id获取客观数据截止时间。",
    annotations=ToolAnnotations(
        title="客观数据截止时间指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_objective_data_end_time(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取客观数据截止时间指标数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客观数据截止时间字段
            sql = "SELECT obj_data_end_time from dp_njc.client_item WHERE id = %s"
            result = db.fetchone(sql, (project_id,))
            if not result or not result.get("obj_data_end_time"):
                raise ToolError("未找到项目客观数据截止时间")
            
            obj_data_end_time = result["obj_data_end_time"]
            
            # 2. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": obj_data_end_time}
                                ]
                            }
                        ]
                    }
                ]
            }
        
        def calc_for_default():
            return calc_for_pd0018()
        
        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()
        
    except Exception as e:
        logger.error(f"客观数据截止时间指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("客观数据截止时间指标数据获取失败")

@mcp.tool(
    name="get_phd_employment_rate",
    description="毕业去向落实率-博士指标数据获取，根据项目id、产品ID等参数，获取博士毕业去向落实率数据。",
    annotations=ToolAnnotations(
        title="毕业去向落实率-博士数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_phd_employment_rate(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取毕业去向落实率-博士数据。
    此指标为客观数据指标，查询学生客观表，毕业去向落实率选取毕业去向合并字段不为'待就业'和'暂不就业'的为落实人数，
    总人数为毕业生人数，其二者占比则为客观数据的毕业去向落实率。注意此指标限定为了学历为博士研究生。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询落实率：毕业去向合并字段不为'待就业'和'暂不就业'为落实人数，总人数为毕业生人数
            sql = f"""
                SELECT
                  ROUND(
                    (SUM(CASE WHEN grad_dest_merge NOT IN ('待就业', '暂不就业') THEN 1 ELSE 0 END) / COUNT(*)),
                    6
                  ) AS ratio_percent
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND education = '博士研究生'
            """
            ans_info = db.fetchone(sql, (shard_tb_key,))
            ratio = ans_info["ratio_percent"] if ans_info and ans_info["ratio_percent"] is not None else 0

            # 5. 返回数据JSON，结构与其他产品一致
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{ratio:.6f}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"博士毕业去向落实率数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("博士毕业去向落实率数据获取失败")

@mcp.tool(
    name="get_graduate_employment_rate_college_top3_phd",
    description="毕业去向落实率-院系前3-博士指标数据获取，根据项目id、产品ID等参数，获取博士研究生学历的毕业去向落实率最高的三个院系及其值。",
    annotations=ToolAnnotations(
        title="毕业去向落实率-院系前3-博士指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_graduate_employment_rate_college_top3_phd(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取博士研究生学历的毕业去向落实率最高的三个院系及其值。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询落实率最高的三个院系
            sql = f"""
                SELECT
                  college,
                  ROUND(
                    (SUM(CASE WHEN grad_dest_merge NOT IN ('待就业', '暂不就业') THEN 1 ELSE 0 END) / COUNT(*)),
                    6
                  ) AS ratio_percent
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND education = '博士研究生'
                GROUP BY college
                ORDER BY ratio_percent DESC
                LIMIT 3
            """
            results = db.fetchall(sql, (shard_tb_key,))

            # 5. 构造返回数据JSON
            calc_vals = []
            for row in results:
                calc_vals.append({
                    "wdValNameList": [row["college"]],
                    "val": [{"key": "default", "val": f"{row['ratio_percent']:.6f}"}]
                })

            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyCode": "WDK00004", "wdKeyName": "院系"}
                        ],
                        "calcVals": calc_vals
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"毕业去向落实率-院系前3-博士数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("毕业去向落实率-院系前3-博士数据获取失败")

@mcp.tool(
    name="get_graduate_employment_rate_top3_phd",
    description="毕业去向落实率-专业前3-博士指标数据获取，根据项目id获取博士研究生学历中毕业去向落实率最高的三个专业及其值。",
    annotations=ToolAnnotations(
        title="毕业去向落实率-专业前3-博士指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_graduate_employment_rate_top3_phd(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取博士研究生学历中毕业去向落实率最高的三个专业及其值。
    查询学生客观表，毕业去向合并字段不为'待就业'和'暂不就业'的为落实人数，
    总人数为毕业生人数，其二者占比则为客观数据的毕业去向落实率。
    限定学历为博士研究生，并按专业spec展开，最后取落实率最高的三个专业及其值。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询落实率最高的三个专业
            sql = f"""
                SELECT
                  spec,
                  ROUND(
                    (SUM(CASE WHEN grad_dest_merge NOT IN ('待就业', '暂不就业') THEN 1 ELSE 0 END) / COUNT(*)),
                    6
                  ) AS ratio_percent
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND education = '博士研究生'
                GROUP BY spec
                ORDER BY ratio_percent DESC
                LIMIT 3
            """
            results = db.fetchall(sql, (shard_tb_key,))

            # 5. 构造返回数据
            calc_vals = []
            for row in results:
                calc_vals.append({
                    "wdValNameList": [row["spec"]],
                    "val": [{"key": "default", "val": f"{row['ratio_percent']:.6f}"}]
                })

            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "wdKeyNameList": [
                            {"wdKeyCode": "WDK00005", "wdKeyName": "专业"}
                        ],
                        "calcVals": calc_vals
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"毕业去向落实率-专业前3-博士数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("毕业去向落实率-专业前3-博士数据获取失败")

@mcp.tool(
    name="get_country_base_employment_distribution",
    description="基层就业情况分布-总体前2指标数据获取，根据项目id获取基层就业类型占比最高的两个类型及其占比。",
    annotations=ToolAnnotations(
        title="基层就业情况分布-总体前2指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_country_base_employment_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取基层就业情况分布-总体前2指标数据。
    查询学生客观表，限定毕业去向合并不为待就业和暂不就业，且基层就业字段country_base_project不为空，
    按country_base_project展开人数占比所有就业人数，最后取基层类型占比最高的两个基层及其占比值。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各基层类型就业人数占比
            sql = f"""
                SELECT 
                    country_base_project, 
                    ROUND(count(*)/(SELECT count(*) FROM {student_table} 
                            WHERE shard_tb_key = %s 
                            AND grad_dest_merge != '待就业' 
                            AND grad_dest_merge != '暂不就业'), 4) AS ratio_val 
                FROM {student_table}
                WHERE shard_tb_key = %s 
                AND grad_dest_merge != '待就业' 
                AND grad_dest_merge != '暂不就业' 
                AND country_base_project != '' 
                GROUP BY country_base_project 
                ORDER BY ratio_val DESC
                LIMIT 2
            """
            results = db.fetchall(sql, (shard_tb_key, shard_tb_key))
            
            # 5. 格式化结果
            if len(results) == 0:
                formatted_result = "无基层就业数据"
            elif len(results) == 1:
                formatted_result = f"{results[0]['country_base_project']}，占比为{results[0]['ratio_val']*100:.2f}%"
            else:
                formatted_result = (
                    f"{results[0]['country_base_project']}，占比为{results[0]['ratio_val']*100:.2f}%；"
                    f"其次为{results[1]['country_base_project']}（{results[1]['ratio_val']*100:.2f}%）"
                )

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": formatted_result}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"基层就业情况分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("基层就业情况分布数据获取失败")

@mcp.tool(
    name="get_doctor_base_employment_distribution",
    description="基层就业情况分布-博士前1指标数据获取，根据项目id获取博士研究生基层就业类型占比最高的类型及其占比值。",
    annotations=ToolAnnotations(
        title="基层就业情况分布-博士前1指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_doctor_base_employment_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取博士研究生基层就业类型占比最高的类型及其占比值。
    限定毕业去向合并不为待就业和暂不就业，且基层就业字段country_base_project不为空，
    按country_base_project展开人数占比所有就业人数，最后取基层类型占比最高的基层及其占比值。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各基层类型就业人数占比
            sql = f"""
                SELECT 
                    country_base_project, 
                    ROUND(count(*)/(SELECT count(*) FROM {student_table} 
                                   WHERE shard_tb_key = %s 
                                   AND grad_dest_merge != '待就业' 
                                   AND grad_dest_merge != '暂不就业' 
                                   AND education = '博士研究生'), 4) * 100 AS ratio_val 
                FROM {student_table} 
                WHERE shard_tb_key = %s 
                  AND grad_dest_merge != '待就业' 
                  AND grad_dest_merge != '暂不就业' 
                  AND country_base_project != '' 
                  AND education = '博士研究生'
                GROUP BY country_base_project 
                ORDER BY ratio_val DESC 
                LIMIT 1
            """
            ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
            
            if not ans_info:
                return {
                    "success": True,
                    "message": "ok",
                    "code": 0,
                    "result": [
                        {
                            "calcVals": [
                                {
                                    "val": [
                                        {"key": "default", "val": "无基层就业数据"}
                                    ]
                                }
                            ]
                        }
                    ]
                }

            top_type = ans_info["country_base_project"]
            ratio = ans_info["ratio_val"]

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{top_type}，占比为{ratio:.2f}%"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"基层就业情况分布-博士前1指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("基层就业情况分布-博士前1指标数据获取失败")

@mcp.tool(
    name="get_phd_middle_region_employment",
    description="中部就业人数、占比描述-博士指标数据获取，根据项目id获取博士研究生在中部地区的就业人数及占比。",
    annotations=ToolAnnotations(
        title="中部就业人数、占比描述-博士指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_phd_middle_region_employment(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取博士研究生在中部地区的就业人数及占比数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询中部地区博士就业人数及占比
            sql = f"""
                SELECT 
                    four_zone, 
                    count(*) as count, 
                    ROUND(count(*)/(SELECT count(*) FROM {student_table} 
                                   WHERE shard_tb_key = %s 
                                   AND grad_dest_merge NOT IN ('待就业', '暂不就业') 
                                   AND education = '博士研究生' 
                                   AND four_zone != '') * 100, 2) AS ratio_val 
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
                AND education = '博士研究生'
                AND four_zone = '中部地区'
                GROUP BY four_zone
            """
            ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
            
            if not ans_info:
                result_str = "共0人，占该学历就业总人数的比例为0%"
            else:
                result_str = f"共{ans_info['count']}人，占该学历就业总人数的比例为{ans_info['ratio_val']}%"

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": result_str}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"中部就业博士数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("中部就业博士数据获取失败")

@mcp.tool(
    name="get_west_employment_doctor",
    description="西部就业人数、占比描述-博士指标数据获取，根据项目id获取西部地区博士研究生就业人数及占比。",
    annotations=ToolAnnotations(
        title="西部就业人数、占比描述-博士指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_west_employment_doctor(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取西部地区博士研究生就业人数及占比数据。
    限定毕业去向合并不为待就业和暂不就业，且四大区域字段four_zone不为空，
    计算西部地区就业人数及其占所有就业人数的比例。
    """
    try:
        # 1. 查询项目表，获取客户编码和项目年份
        sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
            raise ToolError("未找到项目配置信息")
        client_code = proj_info["client_code"]
        item_year = proj_info["item_year"]

        # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
        import re
        match = re.search(r"CL(0*)(\d+)", client_code)
        shard_tb_key = match.group(2) if match else client_code

        # 3. 构造学生客观表名，拼接年份后缀
        student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

        # 4. 查询西部地区博士研究生就业人数及占比
        sql = f"""
            SELECT 
                four_zone, 
                count(*) as count, 
                count(*)/(SELECT count(*) FROM {student_table} 
                         WHERE shard_tb_key = %s 
                         AND grad_dest_merge NOT IN ('待就业', '暂不就业') 
                         AND education = '博士研究生' 
                         AND four_zone != '') AS ratio_val 
            FROM {student_table}
            WHERE shard_tb_key = %s
              AND grad_dest_merge NOT IN ('待就业', '暂不就业')
              AND education = '博士研究生'
              AND four_zone = '西部地区'
              AND four_zone != ''
            GROUP BY four_zone
        """
        ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
        
        if not ans_info:
            result_text = "共0人，占该学历就业总人数的比例为0%"
        else:
            ratio_percent = ans_info["ratio_val"] * 100 if ans_info["ratio_val"] else 0
            result_text = f"共{ans_info['count']}人，占该学历就业总人数的比例为{ratio_percent:.2f}%"

        # 5. 返回数据JSON
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": result_text}
                            ]
                        }
                    ]
                }
            ]
        }

    except Exception as e:
        logger.error(f"西部就业博士数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("西部就业博士数据获取失败")

@mcp.tool(
    name="get_master_middle_region_employment_rate",
    description="中部就业人数、占比描述-硕士指标数据获取，根据项目id获取中部地区硕士研究生就业人数及占比。",
    annotations=ToolAnnotations(
        title="中部就业人数、占比描述-硕士指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_master_middle_region_employment_rate(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取中部地区硕士研究生就业人数及占比数据。
    限定毕业去向合并不为待就业和暂不就业，且四大区域字段four_zone不为空，
    计算四大区域类型为'中部地区'的就业人数及其占比值。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询中部地区硕士研究生就业人数及占比
            sql = f"""
                SELECT 
                    four_zone, 
                    count(*) as count, 
                    ROUND(count(*)/(SELECT count(*) FROM {student_table} 
                                   WHERE shard_tb_key = %s 
                                   AND grad_dest_merge NOT IN ('待就业', '暂不就业') 
                                   AND education = '硕士研究生' 
                                   AND four_zone != '') * 100, 2) AS ratio_val 
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
                AND four_zone = '中部地区'
                AND education = '硕士研究生'
                GROUP BY four_zone
            """
            ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
            
            if not ans_info:
                count = 0
                ratio_val = 0.00
            else:
                count = ans_info["count"]
                ratio_val = ans_info["ratio_val"]

            # 5. 格式化返回结果
            result_str = f"共{count}人，占该学历就业总人数的比例为{ratio_val}%"

            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": result_str}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"中部地区就业人数占比数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("中部地区就业人数占比数据获取失败")

@mcp.tool(
    name="get_master_west_employment_stats",
    description="西部就业人数、占比描述-硕士指标数据获取，根据项目id获取西部地区硕士研究生就业人数及占比数据。",
    annotations=ToolAnnotations(
        title="西部就业人数、占比描述-硕士指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_master_west_employment_stats(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取西部地区硕士研究生就业人数及占比数据。
    限定毕业去向合并不为待就业和暂不就业，且四大区域字段four_zone为'西部地区'。
    """
    try:
        # 1. 查询项目表，获取客户编码和项目年份
        sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
            raise ToolError("未找到项目配置信息")
        client_code = proj_info["client_code"]
        item_year = proj_info["item_year"]

        # 2. 解析客户编码，转为shard_tb_key
        import re
        match = re.search(r"CL(0*)(\d+)", client_code)
        shard_tb_key = match.group(2) if match else client_code

        # 3. 构造学生客观表名，拼接年份后缀
        student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

        # 4. 查询西部地区硕士研究生就业人数及占比
        sql = f"""
            SELECT 
                four_zone, 
                count(*) as count, 
                count(*)/(SELECT count(*) FROM {student_table} 
                         WHERE shard_tb_key = %s 
                         AND grad_dest_merge NOT IN ('待就业', '暂不就业') 
                         AND education = '硕士研究生' 
                         AND four_zone != '') AS ratio_val 
            FROM {student_table}
            WHERE shard_tb_key = %s
              AND grad_dest_merge NOT IN ('待就业', '暂不就业')
              AND education = '硕士研究生'
              AND four_zone = '西部地区'
            GROUP BY four_zone
        """
        ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
        
        if not ans_info:
            result_text = "共0人，占该学历就业总人数的比例为0%"
        else:
            count = ans_info["count"]
            ratio_percent = ans_info["ratio_val"] * 100 if ans_info["ratio_val"] else 0
            result_text = f"共{count}人，占该学历就业总人数的比例为{ratio_percent:.2f}%"

        # 5. 返回数据JSON
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": result_text}
                            ]
                        }
                    ]
                }
            ]
        }

    except Exception as e:
        logger.error(f"西部就业人数数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("西部就业人数数据获取失败")

@mcp.tool(
    name="get_bachelor_middle_region_employment_rate",
    description="中部就业人数、占比描述-本科指标数据获取，根据项目id获取中部地区本科毕业生就业人数及占比。",
    annotations=ToolAnnotations(
        title="中部就业人数、占比描述-本科指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_bachelor_middle_region_employment_rate(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取中部地区本科毕业生就业人数及占比数据。
    限定毕业去向合并不为待就业和暂不就业，且四大区域字段four_zone不为空，
    计算四大区域类型为'中部地区'的就业人数及其占比值。
    """
    try:
        # 1. 查询项目表，获取客户编码和项目年份
        sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
            raise ToolError("未找到项目配置信息")
        client_code = proj_info["client_code"]
        item_year = proj_info["item_year"]

        # 2. 解析客户编码，转为shard_tb_key
        import re
        match = re.search(r"CL(0*)(\d+)", client_code)
        shard_tb_key = match.group(2) if match else client_code

        # 3. 构造学生客观表名，拼接年份后缀
        student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

        # 4. 查询中部地区就业人数及占比
        sql = f"""
            SELECT 
                count(*) as count, 
                ROUND(count(*)/(SELECT count(*) FROM {student_table} 
                               WHERE shard_tb_key = %s 
                               AND grad_dest_merge NOT IN ('待就业', '暂不就业') 
                               AND education = '本科毕业生' 
                               AND four_zone != '') * 100, 2) AS ratio_val 
            FROM {student_table}
            WHERE shard_tb_key = %s
              AND grad_dest_merge NOT IN ('待就业', '暂不就业')
              AND education = '本科毕业生'
              AND four_zone = '中部地区'
        """
        ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
        
        if not ans_info:
            raise ToolError("未找到符合条件的就业数据")
            
        count = ans_info.get("count", 0)
        ratio_val = ans_info.get("ratio_val", 0)

        # 5. 格式化返回结果
        result_str = f"共{count}人，占该学历就业总人数的比例为{ratio_val}%"

        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": result_str}
                            ]
                        }
                    ]
                }
            ]
        }

    except Exception as e:
        logger.error(f"中部地区就业数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("中部地区就业数据获取失败")

@mcp.tool(
    name="get_bachelor_west_employment_stats",
    description="西部就业人数、占比描述-本科指标数据获取，根据项目id获取西部地区本科毕业生就业人数及占比数据。",
    annotations=ToolAnnotations(
        title="西部就业人数、占比描述-本科指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_bachelor_west_employment_stats(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取西部地区本科毕业生就业人数及占比数据。
    限定毕业去向合并不为待就业和暂不就业，且四大区域字段four_zone为'西部地区'。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询西部地区本科毕业生就业人数及占比
            sql = f"""
                SELECT 
                    count(*) as count, 
                    count(*)/(SELECT count(*) FROM {student_table} 
                            WHERE shard_tb_key = %s 
                            AND grad_dest_merge NOT IN ('待就业', '暂不就业') 
                            AND education = '本科毕业生' 
                            AND four_zone != '') AS ratio_val
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
                AND education = '本科毕业生'
                AND four_zone = '西部地区'
            """
            ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
            if not ans_info:
                raise ToolError("未查询到西部地区就业数据")
            
            count = ans_info.get("count", 0)
            ratio = ans_info.get("ratio_val", 0)
            
            # 5. 格式化返回结果
            result_str = f"共{count}人，占该学历就业总人数的比例为{ratio*100:.2f}%"

            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": result_str}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"西部就业数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("西部就业数据获取失败")

@mcp.tool(
    name="get_province_employment_ratio",
    description="省内就业占比指标数据获取，根据项目id获取客户所在省份的省内就业人数占比。",
    annotations=ToolAnnotations(
        title="省内就业占比指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_province_employment_ratio(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取省内就业占比指标数据。
    查询学生客观表，限定毕业去向合并不为待就业和暂不就业，且就业省内外字段job_around不为空，
    按job_around展开人数占比所有就业人数，返回所有就业类型的占比数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询就业省内外类型就业人数占比
            sql = f"""
                SELECT 
                    job_around, 
                    count(*) as count, 
                    count(*)/(SELECT count(*) FROM {student_table} 
                            WHERE shard_tb_key = %s 
                            AND grad_dest_merge NOT IN ('待就业', '暂不就业') 
                            AND job_around != '') AS ratio_val 
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
                AND job_around != ''
                GROUP BY job_around
            """
            results = db.fetchall(sql, (shard_tb_key, shard_tb_key))
            
            # 5. 构建返回数据，包含所有就业类型的占比
            calc_vals = []
            for row in results:
                job_type = row["job_around"]
                count = row["count"]
                ratio_val = row["ratio_val"]
                
                calc_vals.append({
                    "key": job_type,
                    "val": f"{ratio_val:.6f}",
                    "count": count
                })
            
            # 如果没有数据，返回默认结构
            if not calc_vals:
                calc_vals = [{"key": "default", "val": "0.000000", "count": 0}]

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": calc_vals
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"省内就业占比数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("省内就业占比数据获取失败")

@mcp.tool(
    name="get_employment_industry_distribution",
    description="就业行业分布-第1指标数据获取，根据项目id、产品ID等参数，获取就业人数占比最高的行业门类数据。",
    annotations=ToolAnnotations(
        title="就业行业分布-第1指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_employment_industry_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取就业行业分布-第1指标数据（就业人数占比最高的行业门类）。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各行业门类就业人数占比
            sql = f"""
                SELECT 
                    indus_class_object, 
                    count(*) as count, 
                    count(*)/(SELECT count(*) FROM {student_table} 
                            WHERE shard_tb_key = %s 
                            AND grad_dest_merge != '待就业' 
                            AND grad_dest_merge != '暂不就业' 
                            AND indus_class_object != '') AS ratio_val 
                FROM {student_table}
                WHERE shard_tb_key = %s
                AND grad_dest_merge != '待就业'
                AND grad_dest_merge != '暂不就业'
                AND indus_class_object != ''
                GROUP BY indus_class_object
                ORDER BY ratio_val DESC
                LIMIT 1
            """
            ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
            top_industry = ans_info["indus_class_object"] if ans_info and ans_info["indus_class_object"] else "未知"

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": top_industry}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"就业行业分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("就业行业分布数据获取失败")

@mcp.tool(
    name="get_graduate_occupation_distribution",
    description="就业职业分布-第1指标数据获取，根据项目id、产品ID等参数，获取就业人数占比最高的职业门类数据。",
    annotations=ToolAnnotations(
        title="就业职业分布-第1指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_graduate_occupation_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取就业职业分布-第1指标数据（职业门类就业人数占比最高的类型）。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各职业门类就业人数占比
            sql = f"""
                SELECT 
                    occ_class_obj, 
                    count(*) as count, 
                    count(*)/(SELECT count(*) FROM {student_table} 
                             WHERE shard_tb_key = %s 
                             AND grad_dest_merge != '待就业' 
                             AND grad_dest_merge != '暂不就业' 
                             AND occ_class_obj != '') AS ratio_val 
                FROM {student_table} 
                WHERE shard_tb_key = %s 
                AND grad_dest_merge != '待就业' 
                AND grad_dest_merge != '暂不就业' 
                AND occ_class_obj != '' 
                GROUP BY occ_class_obj 
                ORDER BY ratio_val DESC 
                LIMIT 1
            """
            ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
            occupation = ans_info["occ_class_obj"] if ans_info and ans_info.get("occ_class_obj") else "未知"

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": occupation}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"就业职业分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("就业职业分布数据获取失败")

@mcp.tool(
    name="get_employment_unit_distribution",
    description="就业单位分布-第1指标数据获取，根据项目id、产品ID等参数，获取就业人数占比最高的单位性质数据。",
    annotations=ToolAnnotations(
        title="就业单位分布-第1指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_employment_unit_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取就业单位分布-第1指标数据（单位性质就业人数占比最高的类型）。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各单位性质就业人数占比
            sql = f"""
                SELECT 
                    company_nature, 
                    count(*) as count, 
                    count(*)/(SELECT count(*) FROM {student_table} 
                            WHERE shard_tb_key = %s 
                            AND grad_dest_merge != '待就业' 
                            AND grad_dest_merge != '暂不就业' 
                            AND company_nature != '') AS ratio_val 
                FROM {student_table}
                WHERE 
                    shard_tb_key = %s 
                    AND grad_dest_merge != '待就业' 
                    AND grad_dest_merge != '暂不就业' 
                    AND company_nature != '' 
                GROUP BY company_nature
                ORDER BY ratio_val DESC
                LIMIT 1
            """
            ans_info = db.fetchone(sql, (shard_tb_key, shard_tb_key))
            
            # 获取占比最高的单位性质
            top_company_nature = ans_info["company_nature"] if ans_info and ans_info.get("company_nature") else "未知"

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": top_company_nature}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"就业单位分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("就业单位分布数据获取失败")

@mcp.tool(
    name="get_college_count",
    description="院系个数-本科指标数据获取，根据项目id获取本科毕业生院系个数。",
    annotations=ToolAnnotations(
        title="院系个数-本科指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_college_count(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取本科毕业生院系个数数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询去重后的院系个数
            sql = f"""
                SELECT 
                  count(distinct college) as count
                FROM {student_table}
                WHERE shard_tb_key = %s 
                  AND education = '本科毕业生'
            """
            ans_info = db.fetchone(sql, (shard_tb_key,))
            count = ans_info["count"] if ans_info and ans_info["count"] is not None else 0

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{count}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"院系个数数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("院系个数数据获取失败")

@mcp.tool(
    name="get_undergraduate_specialty_count",
    description="专业个数-本科指标数据获取，根据项目id获取本科专业个数。",
    annotations=ToolAnnotations(
        title="专业个数-本科指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_undergraduate_specialty_count(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取本科专业个数数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM dp_njc.client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询专业个数（本科毕业生）
            sql = f"""
                SELECT 
                count(distinct spec) as count
                FROM {student_table}
                WHERE shard_tb_key = %s 
                AND education = '本科毕业生'
            """
            ans_info = db.fetchone(sql, (shard_tb_key,))
            count = ans_info["count"] if ans_info and ans_info["count"] is not None else 0

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{count}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"本科专业个数数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("本科专业个数数据获取失败")

@mcp.tool(
    name="get_top2_majors_by_count",
    description="人数占比前2-专业名称-本科指标数据获取，根据项目id获取本科毕业生人数最多的两个专业名称。",
    annotations=ToolAnnotations(
        title="人数占比前2-专业名称-本科指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_top2_majors_by_count(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取本科毕业生人数最多的两个专业名称。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各专业人数并按人数降序排列
            sql = f"""
                SELECT 
                  spec, count(*) as count
                FROM {student_table}
                WHERE shard_tb_key = %s
                  AND education = '本科毕业生'
                GROUP BY spec
                ORDER BY count(*) DESC
                LIMIT 2
            """
            majors = db.fetchall(sql, (shard_tb_key,))
            
            # 5. 格式化结果为"专业1、专业2"的字符串
            top2_majors = "、".join([m["spec"] for m in majors]) if majors else ""

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": top2_majors}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"人数占比前2专业数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("人数占比前2专业数据获取失败")

@mcp.tool(
    name="get_top2_spec_ratio",
    description="人数占比前2-专业占比-本科指标数据获取，根据项目id获取本科毕业生中人数占比前二的专业占比数据。",
    annotations=ToolAnnotations(
        title="人数占比前2-专业占比-本科指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_top2_spec_ratio(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取本科毕业生中人数占比前二的专业占比数据。
    产品ID: PD0018
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM dp_njc.client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各专业人数及占比
            sql = f"""
                SELECT 
                    spec, 
                    count(*) as count, 
                    ROUND(count(*)/(SELECT count(*) FROM {student_table} WHERE shard_tb_key = %s AND education = '本科毕业生')*100, 2) AS ratio_val 
                FROM {student_table} 
                WHERE 
                    shard_tb_key = %s 
                    AND spec != ''
                    AND education = '本科毕业生'  
                GROUP BY spec
                ORDER BY ratio_val DESC
                LIMIT 2
            """
            spec_data = db.fetchall(sql, (shard_tb_key, shard_tb_key))
            
            # 5. 格式化结果为"aa.bb%、cc.dd%"格式
            top2_ratios = "、".join([f"{item['ratio_val']}%" for item in spec_data])

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": top2_ratios}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"人数占比前2-专业占比数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("人数占比前2-专业占比数据获取失败")

@mcp.tool(
    name="get_provincial_employment_rate",
    description="省内生源就业地分布-省内就业占比指标数据获取，根据项目id获取省内生源在省内就业的占比数据。",
    annotations=ToolAnnotations(
        title="省内生源就业地分布-省内就业占比",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_provincial_employment_rate(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取省内生源就业地分布-省内就业占比数据。
    查询学生客观表，限定为省内生源样本，并按就业省内外维度展开，最后取省内就业对应的占比值。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询省内生源就业分布
            sql = f"""
                SELECT 
                job_around,
                count(*) as count,
                count(*)/(SELECT count(*) FROM {student_table} WHERE shard_tb_key = %s and birth_around = '省内生源' and job_around != '') AS ratio_val 
                FROM {student_table}
                WHERE shard_tb_key = %s 
                and birth_around = '省内生源'
                and job_around != ''
                GROUP BY job_around
                ORDER BY count(*) desc
            """
            results = db.fetchall(sql, (shard_tb_key, shard_tb_key))
            
            # 5. 提取省内就业占比
            provincial_ratio = 0.0
            for row in results:
                if row["job_around"] == "省内就业":
                    provincial_ratio = row["ratio_val"]
                    break

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{provincial_ratio:.4f}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"省内生源就业占比数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("省内生源就业占比数据获取失败")

@mcp.tool(
    name="get_out_province_student_job_distribution",
    description="省外生源就业地分布-省内就业占比指标数据获取，根据项目id获取省外生源在省内就业的占比数据。",
    annotations=ToolAnnotations(
        title="省外生源就业地分布-省内就业占比",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_out_province_student_job_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取省外生源就业地分布-省内就业占比指标数据。
    通过查询学生客观表，计算省内就业人数占(省内就业人数+回生源地就业人数+其他省份就业人数)的比例。
    """
    try:
        # 1. 查询项目表，获取客户编码和项目年份
        sql_proj = "SELECT client_code, item_year FROM dp_njc.client_item WHERE id = %s"
        proj_info = db.fetchone(sql_proj, (project_id,))
        if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
            raise ToolError("未找到项目配置信息")
        client_code = proj_info["client_code"]
        item_year = proj_info["item_year"]

        # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
        import re
        match = re.search(r"CL(0*)(\d+)", client_code)
        shard_tb_key = match.group(2) if match else client_code

        # 3. 构造学生客观表名，拼接年份后缀
        student_table = f"dim_client_target_baseinfo_student_calc_{item_year}"

        # 4. 执行三段SQL查询
        # 4.1 计算其他省份就业人数
        sql_other_province = f"""
            SELECT count(*) as other_province_count 
            FROM {student_table} 
            WHERE shard_tb_key=%s 
              AND session in (%s) 
              AND birth_around in ('生源省外','省外生源') 
              AND job_around in ('就业省外','省外就业') 
              AND job_province!=birth_province 
              AND grad_dest_merge NOT IN ('待就业', '暂不就业')
        """
        other_province = db.fetchone(sql_other_province, (shard_tb_key, item_year))

        # 4.2 计算回生源地就业人数
        sql_home_province = f"""
            SELECT count(*) as home_province_count 
            FROM {student_table} 
            WHERE shard_tb_key=%s 
              AND session in (%s) 
              AND birth_around in ('生源省外','省外生源') 
              AND job_around in ('就业省外','省外就业') 
              AND job_province=birth_province 
              AND grad_dest_merge NOT IN ('待就业', '暂不就业')
        """
        home_province = db.fetchone(sql_home_province, (shard_tb_key, item_year))

        # 4.3 计算省内就业人数
        sql_in_province = f"""
            SELECT count(*) as in_province_count 
            FROM {student_table} 
            WHERE shard_tb_key=%s 
              AND session in (%s) 
              AND birth_around in ('生源省外','省外生源') 
              AND job_around in ('就业省内','省内就业') 
              AND grad_dest_merge NOT IN ('待就业', '暂不就业')
        """
        in_province = db.fetchone(sql_in_province, (shard_tb_key, item_year))

        # 5. 计算占比
        total = (other_province.get('other_province_count', 0) + 
                 home_province.get('home_province_count', 0) + 
                 in_province.get('in_province_count', 0))
        ratio = in_province.get('in_province_count', 0) / total if total > 0 else 0

        # 6. 返回数据JSON
        return {
            "success": True,
            "message": "ok",
            "code": 0,
            "result": [
                {
                    "calcVals": [
                        {
                            "val": [
                                {"key": "default", "val": f"{ratio:.4f}"}
                            ]
                        }
                    ]
                }
            ]
        }

    except Exception as e:
        logger.error(f"省外生源就业地分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("省外生源就业地分布数据获取失败")

@mcp.tool(
    name="get_out_province_employment_distribution",
    description="省外生源就业地分布-生源地就业占比指标数据获取，根据项目id获取省外生源回生源地就业占比数据。",
    annotations=ToolAnnotations(
        title="省外生源就业地分布-生源地就业占比指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_out_province_employment_distribution(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取省外生源就业地分布-生源地就业占比指标数据。
    通过查询项目表获取客户编码和项目年份，然后执行三段SQL分别计算其他省份就业人数、回生源地就业人数和省内就业人数，
    最后计算回生源地就业人数占比作为结果值。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM dp_njc.client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 执行三段SQL分别计算三类就业人数
            # 4.1 计算其他省份就业人数
            sql_other_province = f"""
                SELECT count(*) as other_province_count 
                FROM {student_table} 
                WHERE shard_tb_key=%s 
                AND session in (%s) 
                AND birth_around in ('生源省外','省外生源') 
                AND job_around in ('就业省外','省外就业') 
                AND job_province!=birth_province 
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
            """
            other_province = db.fetchone(sql_other_province, (shard_tb_key, item_year))
            
            # 4.2 计算回生源地就业人数
            sql_home_province = f"""
                SELECT count(*) as home_province_count 
                FROM {student_table} 
                WHERE shard_tb_key=%s 
                AND session in (%s) 
                AND birth_around in ('生源省外','省外生源') 
                AND job_around in ('就业省外','省外就业') 
                AND job_province=birth_province 
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
            """
            home_province = db.fetchone(sql_home_province, (shard_tb_key, item_year))
            
            # 4.3 计算省内就业人数
            sql_in_province = f"""
                SELECT count(*) as in_province_count 
                FROM {student_table} 
                WHERE shard_tb_key=%s 
                AND session in (%s) 
                AND birth_around in ('生源省外','省外生源') 
                AND job_around in ('就业省内','省内就业') 
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
            """
            in_province = db.fetchone(sql_in_province, (shard_tb_key, item_year))

            # 5. 计算回生源地就业人数占比
            total = (other_province.get('other_province_count', 0) + 
                    home_province.get('home_province_count', 0) + 
                    in_province.get('in_province_count', 0))
            ratio = home_province.get('home_province_count', 0) / total if total > 0 else 0

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": f"{ratio:.6f}"}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"省外生源就业地分布数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("省外生源就业地分布数据获取失败")

@mcp.tool(
    name="get_total_graduate_count",
    description="总体毕业生人数-文字指标数据获取，根据项目id、产品ID等参数，获取毕业生总人数。",
    annotations=ToolAnnotations(
        title="总体毕业生人数-文字指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_total_graduate_count(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取总体毕业生人数数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询毕业生总人数
            sql = f"SELECT count(*) AS count FROM {student_table} WHERE shard_tb_key = %s"
            ans_info = db.fetchone(sql, (shard_tb_key,))
            count = ans_info["count"] if ans_info and ans_info["count"] is not None else 0

            # 5. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": str(count)}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"总体毕业生人数数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("总体毕业生人数数据获取失败")

@mcp.tool(
    name="get_top3_employment_provinces",
    description="就业省份前3指标数据获取，根据项目id获取毕业生就业人数最多的三个省份。",
    annotations=ToolAnnotations(
        title="就业省份前3指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_top3_employment_provinces(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取毕业生就业人数最多的三个省份数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各就业省就业总人数
            sql = f"""
                SELECT
                  job_province,
                  count(*) AS count
                FROM
                  {student_table}
                WHERE shard_tb_key = %s And job_province != ''
                GROUP BY job_province
                ORDER BY count desc
                LIMIT 3
            """
            provinces_data = db.fetchall(sql, (shard_tb_key,))
            
            # 5. 拼接前三个省份名称
            top_provinces = "、".join([province["job_province"] for province in provinces_data])

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": top_provinces}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"就业省份前3指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("就业省份前3指标数据获取失败")

@mcp.tool(
    name="get_top3_occupation_classes",
    description="主要就业职业-前3指标数据获取，根据项目id、产品ID等参数，获取就业人数最多的三个职业门类数据。",
    annotations=ToolAnnotations(
        title="主要就业职业-前3指标数据获取",
        readOnlyHint=True,
        idempotentHint=True
    )
)
def get_top3_occupation_classes(
    project_id: Annotated[int, Field(description="项目id")],
    product_id: Annotated[str | None, Field(description="产品id，可选")] = None
) -> dict:
    """
    获取就业人数最多的三个职业门类数据。
    """
    try:
        def calc_for_pd0018():
            # 1. 查询项目表，获取客户编码和项目年份
            sql_proj = "SELECT client_code, item_year FROM client_item WHERE id = %s"
            proj_info = db.fetchone(sql_proj, (project_id,))
            if not proj_info or not proj_info.get("client_code") or not proj_info.get("item_year"):
                raise ToolError("未找到项目配置信息")
            client_code = proj_info["client_code"]
            item_year = proj_info["item_year"]

            # 2. 解析客户编码，转为shard_tb_key（如CL0002->12113）
            import re
            match = re.search(r"CL(0*)(\d+)", client_code)
            shard_tb_key = match.group(2) if match else client_code

            # 3. 构造学生客观表名，拼接年份后缀
            student_table = f"dp_njc.dim_client_target_baseinfo_student_calc_{item_year}"

            # 4. 查询各职业门类就业人数，排除待就业和暂不就业的学生
            sql = f"""
                SELECT
                  occ_class_obj,
                  count(*) AS count
                FROM {student_table}
                WHERE shard_tb_key = %s 
                AND occ_class_obj != '' 
                AND grad_dest_merge NOT IN ('待就业', '暂不就业')
                GROUP BY occ_class_obj
                ORDER BY count DESC
                LIMIT 3
            """
            occupation_data = db.fetchall(sql, (shard_tb_key,))
            
            # 5. 提取前三个职业门类名称
            top3_occupations = [item["occ_class_obj"] for item in occupation_data]
            result_str = "、".join(top3_occupations)

            # 6. 返回数据JSON
            return {
                "success": True,
                "message": "ok",
                "code": 0,
                "result": [
                    {
                        "calcVals": [
                            {
                                "val": [
                                    {"key": "default", "val": result_str}
                                ]
                            }
                        ]
                    }
                ]
            }

        def calc_for_default():
            return calc_for_pd0018()

        # 产品ID分发逻辑
        product_logic_map = {
            "PD0018": calc_for_pd0018,
            # 未来可在此添加更多产品ID及其对应处理函数
        }
        logic_func = product_logic_map.get(product_id, calc_for_default)
        return logic_func()

    except Exception as e:
        logger.error(f"主要就业职业-前3指标数据获取异常: {str(e)}", exc_info=True)
        raise ToolError("主要就业职业-前3指标数据获取失败")

# Server execution block
if __name__ == "__main__":
    """
    Server entry point
    
    Runs the MCP server with Server-Sent Events (SSE) transport protocol
    - Automatic tool registration handling
    - Built-in validation and error handling
    - Context-aware execution environment
    """
    try:
        # Start the MCP server with specified transport
        mcp.run(transport="sse")
    except KeyboardInterrupt:
        # Graceful shutdown on Ctrl+C
        logger.info("Shutting down MCP server...")


